Spring Boot 集成 Redis 实现缓存机制
?本文章牵涉到的技术点比较多:spring?Data JPA、Redis、Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对以上这些技术点有一定的了解或者也可以先看看这篇文章,针对文章中实际的技术点在进一步了解(注意,您需要自己下载Redis Server到您的本地,所以确保您本地的Redis可用,这里还使用了MySQL数据库,当然你也可以内存数据库进行测试)。这篇文章会提供对应的Eclipse代码示例,具体大体的分如下几个步骤: (1)新建Java?MavenProject; (2)在pom.xml中添加相应的依赖包; (3)编写Spring Boot启动类; (4)配置application.properties; (5)编写RedisCacheConfig配置类; (6)编写DemoInfo测试实体类; (7)编写DemoInfoRepository持久化类; (8)编写DemoInfoService类; (9)编写DemoInfoController类; (10)测试代码是否正常运行了 (11)自定义缓存key; ? ???????接下来我们看看具体每个步骤具体的操作吧。 ???????这个步骤就不细说,新建一个spring-boot-redis Java mavenproject; ? (2)在pom.xml中添加相应的依赖包; 在Maven中添加相应的依赖包,主要有:springboot 父节点依赖;spring boot web支持;缓存服务spring-context-support;添加redis支持;JPA操作数据库;MysqL 数据库驱动,具体pom.xml文件如下: <project?xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ??xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd"> ?<modelVersion>4.0.0</modelVersion> ? ?<groupId>com.kfit</groupId> ?<artifactId>spring-boot-redis</artifactId> ?<version>0.0.1-SNAPSHOT</version> ?<packaging>jar</packaging> ? ?<name>spring-boot-redis</name> ?<url>http://maven.apache.org</url> ? ?<properties> ???<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> ???<!--?配置JDK编译版本. --> ???????<java.version>1.8</java.version> ?</properties> ? ?<!-- spring boot?父节点依赖, ??????????????引入这个之后相关的引入就不需要添加version配置, ????????????? ?spring boot会自动选择最合适的版本进行添加。 ?????? --> ???????<parent> ??????????????<groupId>org.springframework.boot</groupId> ??????????????<artifactId>spring-boot-starter-parent</artifactId> ??????????????<version>1.3.3.RELEASE</version> ???????</parent> ? ? ?<dependencies> ????????? ??????????<dependency> ?????????????????????<groupId>junit</groupId> ?????????????????????<artifactId>junit</artifactId> ?????????????????????<scope>test</scope> ??????????????</dependency> ? ??????????????<!-- springboot web支持:mvc,aop... --> ??????????????<dependency> ?????????????????????<groupId>org.springframework.boot</groupId> ?????????????????????<artifactId>spring-boot-starter-web</artifactId> ??????????????</dependency> ????????????? ??????????????<!-- ?????????????????????包含支持UI模版(Velocity,FreeMarker,JasperReports), ?????????????????????邮件服务, ?????????????????????脚本服务(JRuby), ?????????????????????缓存Cache(EHCache), ?????????????????????任务计划Scheduling(uartz)。 ????????????? ?--> ??????????????<dependency> ?????? ??????<groupId>org.springframework</groupId> ?????? ??????<artifactId>spring-context-support</artifactId> ?????? ????</dependency> ? ??????????????<!--?添加redis支持--> ??????????????<dependency> ????????????<groupId>org.springframework.boot</groupId> ???????????<artifactId>spring-boot-starter-redis</artifactId> ????????</dependency> ????????????? ??????????????<!-- JPA操作数据库. --> ??????????????<dependency> ?????? ??????<groupId>org.springframework.boot</groupId> ?????? ??????<artifactId>spring-boot-starter-data-jpa</artifactId> ?????? ????</dependency> ????????????? ??????????????<!--?MysqL数据库驱动. --> ??????????????<dependency> ?????? ??????<groupId>MysqL</groupId> ?????? ??????<artifactId>MysqL-connector-java</artifactId> ?????? ????</dependency> ?????? ??? ?????? ????<!--?单元测试. --> ??????????????<dependency> ????????????<groupId>org.springframework.boot</groupId> ????????????<artifactId>spring-boot-starter-test</artifactId> ????????????<scope>test</scope> ????????</dependency> ????????????? ?</dependencies> </project> 上面是完整的pom.xml文件,每个里面都进行了简单的注释。 ? (3)编写Spring Boot启动类(com.kfit.App); package com.kfit; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** ?* Spring Boot启动类; ?* ?* @authorAngel(QQ:412887952) ?* @version v.0.1 ?*/ ? @SpringBootApplication public class App { ?????? /** ?????? ?*-javaagent:.libspringloaded-1.2.4.RELEASE.jar -noverify ?????? ?* @param args ?????? ?*/ ?????? public static voidmain(String[] args) { ????????????? SpringApplication.run(App.class,args); ?????? } } ? (4)配置application.properties; 这里主要是配置两个资源,第一就是数据库基本信息;第二就是redis配置;第三就是JPA的配置; Src/main/resouces/application.properties: ######################################################## ###datasource??配置MysqL数据源; ######################################################## spring.datasource.url?=jdbc:MysqL://localhost:3306/test spring.datasource.username?=?root spring.datasource.password?=?root spring.datasource.driverClassName?=com.MysqL.jdbc.Driver spring.datasource.max-active=20 spring.datasource.max-idle=8 spring.datasource.min-idle=8 spring.datasource.initial-size=10 ? ? ? ######################################################## ###REDIS (RedisProperties) redis基本配置; ######################################################## # database name spring.redis.database=0 # server host1 spring.redis.host=127.0.0.1?? # server password #spring.redis.password= #connection port spring.redis.port=6379 # pool settings ... spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=0 spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1 # name of Redis server #spring.redis.sentinel.master= # comma-separated list of host:portpairs #spring.redis.sentinel.nodes= ? ? ######################################################## ### Java Persistence Api?自动进行建表 ######################################################## # Specify the DBMS spring.jpa.database?=?MysqL # Show or not log for each sqlquery spring.jpa.show-sql?=?true #?hibernate?ddl auto (create,create-drop,update) spring.jpa.hibernate.ddl-auto?=?update # Naming strategy spring.jpa.hibernate.naming-strategy?=org.hibernate.cfg.ImprovedNamingStrategy # stripped before adding them tothe entity manager) spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MysqL5Dialect ? ? ? (5)编写RedisCacheConfig配置类; ???????缓存主要有几个要实现的类:其一就是CacheManager缓存管理器;其二就是具体操作实现类;其三就是CacheManager工厂类(这个可以使用配置文件配置的进行注入,也可以通过编码的方式进行实现);其四就是缓存key生产策略(当然Spring自带生成策略,但是在Redis客户端进行查看的话是系列化的key,对于我们肉眼来说就是感觉是乱码了,这里我们先使用自带的缓存策略)。 com.kfit.config/RedisCacheConfig: package?com.kfit.config; ? importorg.springframework.cache.CacheManager; importorg.springframework.cache.annotation.CachingConfigurerSupport; importorg.springframework.cache.annotation.EnableCaching; importorg.springframework.context.annotation.Bean; import?org.springframework.context.annotation.Configuration; importorg.springframework.data.redis.cache.RedisCacheManager; importorg.springframework.data.redis.connection.RedisConnectionFactory; importorg.springframework.data.redis.core.RedisTemplate; ? /** ?*?redis?缓存配置; ?* ?*?注意:RedisCacheConfig这里也可以不用继承 :CachingConfigurerSupport,也就是直接一个普通的Class就好了; ?* ?*?这里主要我们之后要重新实现 key的生成策略,只要这里修改KeyGenerator,其它位置不用修改就生效了。 ?* ?*?普通使用普通类的方式的话,那么在使用@Cacheable的时候还需要指定KeyGenerator的名称;这样编码的时候比较麻烦。 ?* ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ @Configuration @EnableCaching?//启用缓存,这个注解很重要; public?class?RedisCacheConfig?extendsCachingConfigurerSupport { ?????? ?????? ???????/** ?????? ?*?缓存管理器. ?????? ?*?@param?redisTemplate ?????? ?*?@return ?????? ?*/ ???????@Bean ???????public?CacheManagercacheManager(RedisTemplate<?,?>?redisTemplate) { ????????????? CacheManagercacheManager?=?newRedisCacheManager(redisTemplate); ??????????????return?cacheManager; ?????? } ? ?????? ???????/** ?????? ?*?redis模板操作类,类似于jdbcTemplate的一个类; ?????? ?* ?????? ?*?虽然CacheManager也能获取到Cache对象,但是操作起来没有那么灵活; ?????? ?* ?????? ?*?这里在扩展下:RedisTemplate这个类不见得很好操作,我们可以在进行扩展一个我们 ?????? ?* ?????? ?*?自己的缓存类,比如:RedisStorage类; ?????? ?* ?????? ?*?@param?factory :?通过Spring进行注入,参数在application.properties进行配置; ?????? ?*?@return ?????? ?*/ ???????@Bean ???????publicRedisTemplate<String,String> redisTemplate(RedisConnectionFactory?factory) { ????????????? RedisTemplate<String,String>?redisTemplate?=?new?RedisTemplate<String,String>(); ??????????????redisTemplate.setConnectionFactory(factory); ????????????? ??????????????//key序列化方式;(不然会出现乱码;),但是如果方法上有Long等非String类型的话,会报类型转换错误; ??????????????//所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer ??????????????//或者JdkSerializationRedisSerializer序列化方式; //?????????? RedisSerializer<String>redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息; //?????????? redisTemplate.setKeySerializer(redisSerializer); //?????????? redisTemplate.setHashKeySerializer(redisSerializer); ????????????? ??????????????return?redisTemplate; ?????? } ? } 在以上代码有很详细的注释,在这里还是在简单的提下: RedisCacheConfig这里也可以不用继承:CachingConfigurerSupport,也就是直接一个普通的Class就好了;这里主要我们之后要重新实现 key的生成策略,只要这里修改KeyGenerator,其它位置不用修改就生效了。普通使用普通类的方式的话,那么在使用@Cacheable的时候还需要指定KeyGenerator的名称;这样编码的时候比较麻烦。 ? ? (6)编写DemoInfo测试实体类; ???????编写一个测试实体类:com.kfit.bean.DemoInfo: package?com.kfit.bean; ? import?java.io.Serializable; import?javax.persistence.Entity; import?javax.persistence.GeneratedValue; import?javax.persistence.Id; /** ?*?测试实体类,这个随便; ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ @Entity public?class?DemoInfo??implements?Serializable{ ???????private?static?final?long?serialVersionUID?= 1L; ???????@Id?@GeneratedValue ???????private?long?id; ???????private?String?name; ???????private?String?pwd; ???????public?long?getId() { ??????????????return?id; ?????? } ???????public?void?setId(long?id) { ??????????????this.id?=?id; ?????? } ???????public?StringgetName() { ??????????????return?name; ?????? } ???????public?void?setName(String?name) { ??????????????this.name?=?name; ?????? } ???????public?String getPwd(){ ??????????????return?pwd; ?????? } ???????public?void?setPwd(String?pwd) { ??????????????this.pwd?=?pwd; ?????? } ?????? ???????@Override ???????public?StringtoString() { ?????? ???????return?"DemoInfo [id="?+?id?+?",name="?+?name?+?",pwd="?+?pwd?+?"]"; ?????? } } ? (7)编写DemoInfoRepository持久化类; ?????? DemoInfoRepository使用Spirng DataJPA实现: com.kfit.repository.DemoInfoRepository: package?com.kfit.repository; ? import?org.springframework.data.repository.CrudRepository; ? import?com.kfit.bean.DemoInfo; ? /** ?* DemoInfo持久化类 ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ public?interfaceDemoInfoRepository?extends?CrudRepository<DemoInfo,Long> { ? } ? (8)编写DemoInfoService类; ???????编写DemoInfoService,这里有两个技术方面,第一就是使用Spring @Cacheable注解方式和RedisTemplate对象进行操作,具体代码如下: com.kfit.service.DemoInfoService: package?com.kfit.service; ? import?com.kfit.bean.DemoInfo; ? /** ?* demoInfo?服务接口 ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ public?interface?DemoInfoService{ ?????? ???????public?DemoInfofindById(long?id); ?????? ???????public?voiddeleteFromCache(long?id); ? ???????void?test(); } ? ? com.kfit.service.impl.DemoInfoServiceImpl: package?com.kfit.service.impl; ? import?javax.annotation.Resource; ? importorg.springframework.cache.annotation.CacheEvict; importorg.springframework.cache.annotation.Cacheable; importorg.springframework.data.redis.core.RedisTemplate; importorg.springframework.data.redis.core.ValueOperations; importorg.springframework.stereotype.Service; ? import?com.kfit.bean.DemoInfo; importcom.kfit.repository.DemoInfoRepository; import?com.kfit.service.DemoInfoService; ? /** ?* ?*DemoInfo数据处理类 ?* ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ @Service public?class?DemoInfoServiceImpl?implements?DemoInfoService{ ?????? ???????@Resource ???????privateDemoInfoRepository?demoInfoRepository; ?????? ???????@Resource ???????privateRedisTemplate<String,String>?redisTemplate; ?????? ???????@Override ???????public?void?test(){ ????????????? ValueOperations<String,String>valueOperations?=?redisTemplate.opsForValue(); ??????????????valueOperations.set("mykey4",?"random1="+Math.random()); ????????????? System.out.println(valueOperations.get("mykey4")); ?????? } ?????? ???????//keyGenerator="myKeyGenerator" ???????@Cacheable(value="demoInfo")?//缓存,这里没有指定key. ???????@Override ???????public?DemoInfofindById(long?id) { ????????????? System.err.println("DemoInfoServiceImpl.findById()=========从数据库中进行获取的....id="+id); ??????????????return?demoInfoRepository.findOne(id); ?????? } ?????? ???????@CacheEvict(value="demoInfo") ???????@Override ???????public?void?deleteFromCache(long?id) { ????????????? System.out.println("DemoInfoServiceImpl.delete().从缓存中删除."); ?????? } ?????? } ? ? (9)编写DemoInfoController类; package?com.kfit.controller; ? importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.stereotype.Controller; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.ResponseBody; ? import?com.kfit.bean.DemoInfo; import?com.kfit.service.DemoInfoService; ? /** ?*?测试类. ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ @Controller public?class?DemoInfoController { ?????? ???????@Autowired ?????? ?DemoInfoService?demoInfoService; ?????? ? ?????? ???????@RequestMapping("/test") ???????public?@ResponseBody?String test(){ ?????? ??? DemoInfo?loaded?=?demoInfoService.findById(1); System.out.println("loaded="+loaded); DemoInfo?cached?=?demoInfoService.findById(1); ?????? ?? ?System.out.println("cached="+cached); ?????? ????loaded?=?demoInfoService.findById(2); ?????? ??? System.out.println("loaded2="+loaded); ?????? ????return?"ok"; ?????? } ?????? ?????? ???????@RequestMapping("/delete") ???????public?@ResponseBody?String delete(long?id){ ?????? ????demoInfoService.deleteFromCache(id); ?????? ????return?"ok"; ?????? } ?????? ???????@RequestMapping("/test1") ???????public?@ResponseBody?String test1(){ ?????? ????demoInfoService.test(); ?????? ??? System.out.println("DemoInfoController.test1()"); ?????? ????return?"ok"; ?????? } ?????? } ? (10)测试代码是否正常运行了 ? 启动应用程序,访问地址:http://127.0.0.1:8080/test 查看控制台可以查看: DemoInfoServiceImpl.findById()=========从数据库中进行获取的....id=1 loaded=DemoInfo [id=1,name=张三,pwd=123456] cached=DemoInfo [id=1,pwd=123456] DemoInfoServiceImpl.findById()=========从数据库中进行获取的....id=2 loaded2=DemoInfo [id=2,pwd=123456] 如果你看到以上的打印信息的话,那么说明缓存成功了。 ? 访问地址:http://127.0.0.1:8080/test1 random1=0.9985031320746356 DemoInfoController.test1() ? 二次访问:http://127.0.0.1:8080/test loaded=DemoInfo [id=1,pwd=123456] loaded2=DemoInfo [id=2,pwd=123456] 这时候所有的数据都是执行缓存的。 ? 这时候执行删除动作:http://127.0.0.1:8080/delete?id=1 然后在访问:http://127.0.0.1:8080/test DemoInfoServiceImpl.findById()=========从数据库中进行获取的....id=1 loaded=DemoInfo [id=1,pwd=123456] ? (11)自定义缓存key; 在com.kfit.config.RedisCacheConfig类中重写CachingConfigurerSupport中的keyGenerator,具体实现代码如下: ? /** ?????? ?*?自定义key. ?????? ?*?此方法将会根据类名+方法名+所有参数的值生成唯一的一个key,即使@Cacheable中的value属性一样,key也会不一样。 ?????? ?*/ ???????@Override ???????public?KeyGeneratorkeyGenerator() { ????????????? System.out.println("RedisCacheConfig.keyGenerator()"); ??????????????return?new?KeyGenerator(){ ?????????????????????@Override ?????????????????????public?Objectgenerate(Object?o,Method?method,Object...?objects) { ????????????????????????????// This willgenerate a unique key of the class name,the method name ????????????????????????????//and allmethod parameters appended. ??????????????????????????? StringBuildersb?=?newStringBuilder(); ????????????????????????????sb.append(o.getClass().getName()); ????????????????????????????sb.append(method.getName()); ????????????????????????????for?(Object?obj?:?objects) { ??????????????????????????? ???????sb.append(obj.toString()); ??????????????????????????? } ??????????????????????????? System.out.println("keyGenerator="?+?sb.toString()); ????????????????????????????return?sb.toString(); ???????????????????? } ????????????? }; ?????? } ? 这时候在redis的客户端查看key的话还是序列化的肉眼看到就是乱码了,那么我改变key的序列方式,这个很简单,redis底层已经有具体的实现类了,我们只需要配置下: //key序列化方式;(不然会出现乱码;),但是如果方法上有Long等非String类型的话,会报类型转换错误; //所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer //或者JdkSerializationRedisSerializer序列化方式; ????????????? RedisSerializer<String>redisSerializer?=?newStringRedisSerializer();//Long类型不可以会出现异常信息; ??????????????redisTemplate.setKeySerializer(redisSerializer); ??????????????redisTemplate.setHashKeySerializer(redisSerializer); 综上以上分析:RedisCacheConfig类的方法调整为: package?com.kfit.config; ? import?java.lang.reflect.Method; ? importorg.springframework.cache.CacheManager; importorg.springframework.cache.annotation.CachingConfigurerSupport; importorg.springframework.cache.annotation.EnableCaching; importorg.springframework.cache.interceptor.KeyGenerator; import?org.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.data.redis.cache.RedisCacheManager; importorg.springframework.data.redis.connection.RedisConnectionFactory; importorg.springframework.data.redis.core.RedisTemplate; importorg.springframework.data.redis.serializer.RedisSerializer; importorg.springframework.data.redis.serializer.StringRedisSerializer; ? /** ?*?redis?缓存配置; ?* ?*?注意:RedisCacheConfig这里也可以不用继承 :CachingConfigurerSupport,也就是直接一个普通的Class就好了; ?* ?*?这里主要我们之后要重新实现 key的生成策略,只要这里修改KeyGenerator,其它位置不用修改就生效了。 ?* ?*?普通使用普通类的方式的话,那么在使用@Cacheable的时候还需要指定KeyGenerator的名称;这样编码的时候比较麻烦。 ?* ?*?@author?Angel(QQ:412887952) ?*?@version?v.0.1 ?*/ @Configuration @EnableCaching?//启用缓存,这个注解很重要; public?class?RedisCacheConfig?extendsCachingConfigurerSupport { ?????? ???????/** ?????? ?*?缓存管理器. ?????? ?*?@param?redisTemplate ?????? ?*?@return ?????? ?*/ ???????@Bean ???????public?CacheManagercacheManager(RedisTemplate<?,?>?redisTemplate) { ????????????? CacheManagercacheManager?=?newRedisCacheManager(redisTemplate); ??????????????return?cacheManager; ?????? } ? ?????? ???????/** ?????? ?* RedisTemplate缓存操作类,但是如果方法上有Long等非String类型的话,会报类型转换错误; ??????????????//所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer ??????????????//或者JdkSerializationRedisSerializer序列化方式; ????????????? RedisSerializer<String>redisSerializer?=?newStringRedisSerializer();//Long类型不可以会出现异常信息; ??????????????redisTemplate.setKeySerializer(redisSerializer); ??????????????redisTemplate.setHashKeySerializer(redisSerializer); ????????????? ??????????????return?redisTemplate; ?????? } ?????? ???????/** ?????? ?*?自定义key. ?????? ?*?此方法将会根据类名+方法名+所有参数的值生成唯一的一个key,the method name ????????????????????????????//and allmethod parameters appended. ??????????????????????????? StringBuildersb?=?newStringBuilder(); ????????????????????????????sb.append(o.getClass().getName()); ????????????????????????????sb.append(method.getName()); ????????????????????????????for?(Object?obj?:?objects) { ???????????????????????????????????sb.append(obj.toString()); ??????????????????????????? } ??????????????????????????? System.out.println("keyGenerator="?+?sb.toString()); ????????????????????????????return?sb.toString(); ???????????????????? } ????????????? }; ?????? } ? } ? 这时候在访问地址:http://127.0.0.1:8080/test 这时候看到的Key就是:com.kfit.service.impl.DemoInfoServiceImplfindById1 在控制台打印信息是: (1)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById1 (2)DemoInfoServiceImpl.findById()=========从数据库中进行获取的....id=1 (3)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById1 (4)loaded=DemoInfo[id=1,pwd=123456] (5)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById1 (6)cached=DemoInfo[id=1,pwd=123456] (7)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById2 (8)keyGenerator=com.kfit.service.impl.DemoInfoServiceImplfindById2 (10)DemoInfoServiceImpl.findById()=========从数据库中进行获取的....id=2 (11)loaded2=DemoInfo[id=2,pwd=123456] 其中@Cacheable,@CacheEvict下节进行简单的介绍,这节的东西实在是太多了,到这里就打住吧,剩下的就需要靠你们自己进行扩展了。 (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |