javaee论坛

普通会员

225648

帖子

335

回复

349

积分

楼主
发表于 2019-11-03 06:53:34 | 查看: 753 | 回复: 2

前面有介绍过spring整合redis和redis的一些注意点,在实际开发中,springcache方便简化的缓存操作的大部分内容。通过注解的方式实现缓存。阅读前推荐先阅读:redis缓存介绍。和springboot整合redis

缓存抽象的核心是将缓存应用于Java方法,从而根据缓存中可用的信息减少执行次数。也就是说,每次调用目标方法时,抽象都会应用一种缓存行为,该行为检查该方法是否已针对给定参数执行。

如果已执行,则返回缓存的结果,而不必执行实际方法。如果该方法尚未执行,则执行该方法,并将结果缓存并返回给用户,以便下次调用该方法时,返回缓存的结果。这样,对于给定的一组参数,昂贵的方法(无论是CPU还是IO)只能执行一次,并且重用结果而不必再次实际执行该方法。

在缓存框架出现之前,要么公司自己写缓存框架造轮子,要么在每个service中手动修改调用redis进行存储或者更新(删除).一旦业务逻辑代码有变,将造成不小的维护负担。并且代码量也很大,使得项目看起来很臃肿!

而springcache的出现用于简化对缓存的操作,你只需要配置缓存的cacheManager,然后按照一些简单的序列化配置,在service层即可使用注解的形式对项目逻辑无侵入的调用缓存。

方法介绍@Cacheable:

触发​​缓存填充。即:如果有缓存从缓存中直接拿数据(就不走service了),如果没缓存那么就执行service。将service中的值传到缓存中(执行service)。

多个名称:缓存可能有一个名字或者多个名字,那么你可以@Cacheable("books")或者@Cacheable({"books","isbns"})表示。自定义key:大家对同一个接口进行查询,可能有多个参数,那么你可以自定义key。类似@Cacheable(value="getuser",key="#username")这样写法,这个规则和mybatis注解很像,但也有点区别也可以自行拼凑。条件缓存:有些数据你若不想所有都想走缓存,当然也可以设置条件缓存,比如你若想让两个字的姓名缓存,那么你可以@Cacheable(value="getuser",key="#username",condition="#username.length()<3")这样写法上锁:@Cacheable(value="getuser",key="#username",sync=true)对有些数据,你可能不想让他并发执行,那么可以假设sync关键字。当然,如果是分布式或者多台服务器需要考虑分布式锁实现。@Cacheable(value="getalluser")publicList<user>getalluser(){/*假设数据从数据库查出啦。有这么两条*/useruser1=newuser("bigsai","12345","man");useruser2=newuser("给我star","谢谢","man");List<user>list=newArrayList<>();list.add(user1);list.add(user2);returnlist;}//获取user如果没有缓存将执行log@Cacheable(value="getuser",key="#username")publicusergetuserbyname(Stringusername){//假设该数据从mysql查询结果如下useruser=newuser(username,"123","women");logger.info("执行方法cacheable,从mysql查询");returnuser;}@CacheEvict:

触发​​缓存逐出。即删除缓存。执行之后将删除缓存。

用法和前面相似@CacheEvict(value="getuser",key="#username")但是若想删除getuser下所有缓存目录,那么加个参数@CacheEvict(value="getuser",key="#username",allEntries=true)其中allEntries就是删除所有的意思。当需要清除整个缓存区域时,此选项会派上用场。而不是逐出每个条目。@CachePut:

更新缓存而不会干扰方法执行。即:不管有没有缓存。每次都执行方法,将方法得到的数据更新到缓存之中。起到update的功能。

需要注意的是,在更新过程中可能有并发的存在。你可以设置锁.参考Cacheable用法@Caching:

重新组合要应用于方法的多个缓存操作。

有时,需要指定相同类型(例如@CacheEvict或@CachePut)的多个注释-例如,因为不同高速缓存之间的条件或键表达式不同。@Caching允许多个嵌套@Cacheable,@CachePut和@CacheEvict注解相同的方法来使用。以下示例使用两个@CacheEvict注释:@Caching(evict={@CacheEvict("primary"),@CacheEvict(cacheNames="secondary",key="#p0")})此操作可能是一个方法操作更新、删除不同的缓存。具体使用需要考虑情景。@CacheConfig:

在类级别共享一些常见的缓存相关设置。

此方法针对同一个类中一些共同操作,简便操作和代码量。到目前为止,我们已经看到缓存操作提供了许多自定义选项,您可以为每个操作设置这些选项。但是,如果某些自定义选项适用于该类的所有操作,则它们可能会很繁琐。例如,指定用于类的每个高速缓存操作的高速缓存的名称可以由单个类级定义替换。这是@CacheConfig发挥作用的地方。以下示例用于@CacheConfig设置缓存的名称:@CacheConfig("books")publicclassBookRepositoryImplimplementsBookRepository{@CacheablepublicBookfindBook(ISBNisbn){...}}具体整合

step1:创建springboot工程,省略部分截图step2:添加maven依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

step3:配置application.properties

spring.redis.host=127.0.0.1spring.redis.password=spring.redis.port=6379spring.redis.timeout=10000spring.cache.type=redislogging.level.com.redisCache=debugspring.redis.database=3spring.redis.lettuce.pool.max-active=8spring.redis.lettuce.pool.max-wait=-1spring.redis.lettuce.pool.max-idle=8spring.redis.lettuce.pool.min-idle=0

step4:config目录下建立配置文件(缓存注入对象配置,以及序列化相关配置,和前文redis差不多)

这里面需要注意的是里面有关于redis的一些配置。在项目中防止多个项目公用一个redis可以在redis前面加个前缀(通常是项目名)。过期时间一定要设置,并且过期策略还需要根据项目需求具体踩坑设置。packagecom.redisCache.config;importorg.springframework.boot.autoconfigure.AutoConfigureAfter;importorg.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;importorg.springframework.cache.CacheManager;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.Primary;importorg.springframework.data.redis.cache.RedisCacheConfiguration;importorg.springframework.data.redis.cache.RedisCacheManager;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;importorg.springframework.data.redis.serializer.RedisSerializationContext;importorg.springframework.data.redis.serializer.StringRedisSerializer;importjava.io.Serializable;importjava.time.Duration;importjava.util.*;@Configuration@AutoConfigureAfter(RedisAutoConfiguration.class)publicclassRedisConfiguration{@BeanpublicRedisTemplate<String,Serializable>redisTemplate(LettuceConnectionFactoryredisConnectionFactory){RedisTemplate<String,Serializable>template=newRedisTemplate<>();template.setKeySerializer(newStringRedisSerializer());template.setValueSerializer(newGenericJackson2JsonRedisSerializer());template.setConnectionFactory(redisConnectionFactory);returntemplate;}@Bean(name="cacheManager")@PrimarypublicCacheManagercacheManager(RedisConnectionFactoryredisConnectionFactory){RedisCacheConfigurationcacheConfiguration=RedisCacheConfiguration.defaultCacheConfig()//.entryTtl(Duration.ofSeconds(50))设置过期时间.disableCachingNullValues().computePrefixWith(cacheName->"rediscache".concat(":").concat(cacheName).concat(":"))//rediscache可以改成你的项目名.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(newStringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()));returnRedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();}}

step5:创建pojo对象和controllerpojo:

packagecom.redisCache.pojo;importjava.io.Serializable;publicclassuserimplementsSerializable{privateStringname;privateStringpassword;privateStringsex;publicuser(Stringname,Stringpassword,Stringsex){this.name=name;this.password=password;this.sex=sex;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetPassword(){returnpassword;}publicvoidsetPassword(Stringpassword){this.password=password;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}}

service:

packagecom.redisCache.service;importcom.redisCache.pojo.user;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.cache.annotation.CacheConfig;importorg.springframework.cache.annotation.CacheEvict;importorg.springframework.cache.annotation.CachePut;importorg.springframework.cache.annotation.Cacheable;importorg.springframework.stereotype.Service;importjava.util.ArrayList;importjava.util.List;@ServicepublicclassredisService{Loggerlogger=LoggerFactory.getLogger(redisService.class);@Cacheable(value="getalluser")publicList<user>getalluser(){/*假设数据从数据库查出啦。有这么两条*/useruser1=newuser("bigsai","12345","man");useruser2=newuser("给我star","谢谢","man");List<user>list=newArrayList<>();list.add(user1);list.add(user2);returnlist;}//获取user如果没有缓存将执行log@Cacheable(value="getuser",key="#username")publicusergetuserbyname(Stringusername){//假设该数据从mysql查询结果如下useruser=newuser(username,"123","women");logger.info("执行方法cacheable,从mysql查询");returnuser;}//更新user。每次都执行@CachePut(value="getuser",key="#username")publicuserupdateuser(Stringusername,Stringpassword){//假设更新用户账号密码reuseruser=newuser(username,"123","women");user.setPassword(password);logger.info("执行方法cacheput,再数据库更新返回");returnuser;}//删除缓存,其中condition可加可不加,本句意思是只有当姓名为bigsai@CacheEvict(value="getuser",key="#username",allEntries=true)publicStringdeleteuser(Stringusername){return"移除成功";}}

controller:

packagecom.redisCache.controller;importcom.redisCache.pojo.user;importcom.redisCache.service.redisService;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RestController;importjava.util.List;@RestControllerpublicclassredisController{privatefinalstaticLoggerlog=LoggerFactory.getLogger(redisController.class);@Autowired(required=false)redisServiceredisService;@GetMapping("getalluser")publicList<user>getalluser(){returnredisService.getalluser();}@GetMapping("getuser/{username}")publicusergetuser(@PathVariableStringusername){returnredisService.getuserbyname(username);}@GetMapping("updateuser/{username}/{password}")publicuserupdateuser(@PathVariableStringusername,@PathVariableStringpassword){returnredisService.updateuser(username,password);}@GetMapping("deleteuser/{username}")publicStringdeleteuser(@PathVariableStringusername){returnredisService.deleteuser(username);}}

step6:在启动类加上允许缓存注解

测试与总结

getalluser:getuser:update:更新上一条delete:整个移除(前面讲到allentries参数作用,不妨自己测试)

可以看得到缓存的一些操作正常完成了上文只是一个简单的整合过程,具体使用还需要自己踩坑才行。而缓存的深度远远不是这么简单,还需要自己挖掘。项目完整github地址。不吝啬star的大哥求个star。最后,欢迎关注个人公众号:bigsai关注后回复java有精选资料一份!

普通会员

0

帖子

344

回复

348

积分
沙发
发表于 2019-11-22 05:58:52

我喜欢

普通会员

1

帖子

303

回复

311

积分
板凳
发表于 2023-10-07 15:04:31

百因必有果你的报应就是我

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017