如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的

发布时间:2021-09-29 16:20:34 作者:柒染
来源:亿速云 阅读:174

这期内容当中小编将会给大家带来有关如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

简介

关于项目中使用缓存的方式各不相同,今天来完成通过@Cacheable 注解完成方法调用拉去缓存的操作内容;首先@Cacheable 注解生成的数据类型就是key-value类型的;只是会多生成一个

项目demo下载

代码展示

1.pox.xml

引入redis的start maven配置
<!-- redis 依赖 --><dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-redis</artifactId>  <version>1.4.3.RELEASE</version></dependency>

2.application.properties文件

配置springboot默认的redis 的配置项内容;测试使用的额的是单机环境

spring.redis.database=0  
 spring.redis.host=127.0.0.1
 spring.redis.port=6379  
 spring.redis.password=khanyu
 spring.redis.pool.max-active=8  
 spring.redis.pool.max-wait=-1  
 spring.redis.pool.max-idle=8  
 spring.redis.pool.min-idle=0  
 spring.redis.timeout=5000

3.RedisCacheableConfig缓存配置类内容;

首先redis默认的key和value序列化方式不太方便在工具类中展示所以使用自定义的序列化方式处理key和value值 
FastjsonRedisSerializer 是自定义的序列化方式类,通过fastJson处理
RedisConnectionFactory 是springboot默认通过加载配置给初始化的
@Beanpublic RedisTemplate<String, String> redisTemplate(@Autowired RedisConnectionFactory factory) {   StringRedisTemplate template = new StringRedisTemplate(factory);template.setKeySerializer(new StringRedisSerializer());//设置redis中缓存的key 的序列化/反序列化方式template.setValueSerializer(new FastjsonRedisSerializer());//设置redis 中value 的序列化/反序列化方式template.afterPropertiesSet();return template;}
这里配置的是RedisCacheManager 实现@Cacheable注解内容的cacheManager 属性
指定缓存时指定到redis中 还是我们本地的内存中 这个里面配置的是redis中的配置
里面的Map中配置的是各个key 值缓存的时间,然后默认的key配置时间
@Bean("redisCacheManager")@Primarypublic RedisCacheManager initRedisCacheManager(@Autowired RedisTemplate redisTemplate){   
		RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
		Map<String, Long> expires = new HashMap<>();
		expires.put("user",1000L);//设置redis中指定key 的缓存时间
		expires.put("khy",1000L);//设置redis中指定key 的缓存时间//.... 每个key 都可以在这里设置
		redisCacheManager.setExpires(expires);
		redisCacheManager.setDefaultExpiration(1500);//设置默认的key缓存失效时间return redisCacheManager;}
配置的是默认的本地的缓存内容配置方式
/** * 设置缓存到本地配置的FactoryBean * @author khy * @createTime 2020年11月18日下午3:43:47 * @return */@Beanpublic ConcurrentMapCacheFactoryBean initConcurrentMapCacheFactoryBean(){   
		ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean();
		cacheFactoryBean.setName("khy");return cacheFactoryBean;}/** * @Cacheable 注解中cacheManager 指定对应的manager类型 是本地还是依赖Redis * @author khy * @createTime 2020年11月18日下午3:46:21 * @param concurrentMapCache * @return */@Bean("simpleCacheManager")public SimpleCacheManager initLocalCacheManager(@Autowired ConcurrentMapCache concurrentMapCache){   
		SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
		List<Cache> caches = new ArrayList<Cache>(){   {   add(concurrentMapCache);}};
		simpleCacheManager.setCaches(caches);return simpleCacheManager;}
配置的是自定义的缓存key 的生成方式;
/** * 这个是设置前缀的方式 * 下面的代码会用到的; * @author khy * @createTime 2020年10月22日上午10:20:02 * @return */@Bean("prefixKeyGenerator")public PrefixKeyGenerator initPrefixKeyGenerator(){   
		PrefixKeyGenerator prefixKeyGenerator = new PrefixKeyGenerator("prefix");return prefixKeyGenerator;}
自定义缓存key的生成器
public class PrefixKeyGenerator extends SimpleKeyGenerator {   private static final String SLOT_CHANGER = "{Qi}";private static final String COLON = ":";private String module;public PrefixKeyGenerator(String module) {   this.module = module;}/** * 自定义的前缀设置(通过将传递的对象转化成key然后进行拼接使用) * * ToStringStyle.SHORT_PREFIX_STYLE * 这个是无类前缀的toString样式,使用User实例用ToStringBuilder类输出的结果是 */public Object generate(Object target, Method method, Object... params) {   
		StringBuilder key = new StringBuilder();
		Object result = super.generate(target, method, params);if (result instanceof String && JedisClusterCRC16.getSlot((String) result) == 0) {   
			result = result + "{Qi}";}// 拼接的是 前缀:方法class类.方法名:result//如果不同包下面出现相同的class相同名称的则需要获取target.getClass().getName(); 全路径的class名称
		key.append(this.module).append(":").append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":").append(result);return key.toString();}}

4.CacheableController

通过请求本地的代码实现将缓存设置到redis中
http://localhost:8080/cacheable/getSimpleString1?userName=candy
http://localhost:8080/cacheable/getSimpleString1?userName=candy2
图1.在工具中查看到缓存值是根据我们的请求参数是的值来进行缓存的
再次去请求下面的链接因为缓存效果是一样的所以不会再进入方法中
http://localhost:8080/cacheable/getSimpleString2?userName=candy
http://localhost:8080/cacheable/getSimpleString2?userName=candy2

图2.调用下面的方法会在两个zset缓存
http://localhost:8080/cacheable/getSimpleString3?userName=candy2

如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的
如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的

/** * 最简单的缓存设置方式 * 请求指定参数内容;然后缓存响应的结果到redis缓存中去 * 当前缓存会设置两种缓存格式 * 一种是String 类型请求参数中的 userName的取值 value 是响应结果 * 一种是zset格式 key =khy~keys row是userName 的值 * * 同时因为指定的value都是 khy 在 RedisCacheableConfig 中的 RedisCacheManager 配置的 khy 的缓存存活时间是1000s * 所以两种缓存的有效时间都是1000; * * 第一次请求 http://localhost:8080/cacheable/getSimpleString1?userName=candy * 则方法中的 内容会被打印 但是在此请求的时间 因为userName=candy参数不变则直接从缓存中获取,不再进入方法 * 虽然多次请求但是对应的String 类型的缓存的candy的有效时间是不会被更新的 * * 后面请求http://localhost:8080/cacheable/getSimpleString1?userName=candy1 * 则 新插入String类型的缓存 同事更新 khy~keys 中数据新增一条; * @author khy * @createTime 2020年10月22日上午10:28:23 * @param userName * @return */@RequestMapping("/getSimpleString1")@Cacheable(cacheManager="redisCacheManager" ,value="khy")public String getSimpleString1(String userName){   
		 System.out.println("getSimpleString 进入方法执行了"+userName);return "返回的userName="+userName;}/** * 和getSimpleString1 返回的结果是一致的; * @author khy * @createTime 2020年10月22日上午10:52:31 * @param userName * @return */@RequestMapping("/getSimpleString2")@Cacheable(cacheManager="redisCacheManager" ,value="khy" ,key="#userName")public String getSimpleString2(String userName){   
		System.out.println("getSimpleString2 进入方法执行了"+userName);return "返回的userName="+userName;}/** * String 类型的缓存和getSimpleString1 返回的结果是一致的; * 但是因为value 指定了两种 ;所以生成2中的 zset * 分别 是 khy~keys 和 111~keys * 因为 RedisCacheableConfig 中 RedisCacheManager 配置的默认时间是1500秒 * @author khy * @createTime 2020年10月22日上午11:00:53 * @param userName * @return */@RequestMapping("/getSimpleString3")@Cacheable(cacheManager="redisCacheManager" ,value={   "khy","111"})public String getSimpleString3(String userName){   
		System.out.println("getSimpleString3 进入方法执行了"+userName);return "返回的userName="+userName;}
2.多请求参数的缓存的key 通过表达式获取请求参数中的值作为key 来实现缓存的
http://localhost:8080/cacheable/getSimpleString5?userName=candy&userId=111
http://localhost:8080/cacheable/getSimpleString5?userName=candy&userId=1112
如果直接用对象去接受参数,则通过表达式获取对象中的某个属性作为key
http://localhost:8080/cacheable/getSimpleEntity4?userName=candy&password=1112

如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的

/** * 虽然传递多个请求参数 但是key 中指定的#userName 作为key值 * 则主要是根据userName的值 虽然后面的userId参数修改但是也不会进入当前方法 * @author khy * @createTime 2020年10月22日上午11:39:03 * @param userId * @param userName * @return */@RequestMapping("/getSimpleString4")@Cacheable(cacheManager="redisCacheManager" ,value="khy",key="#userName")public String getSimpleString4(Integer userId,String userName){   
		System.out.println("getSimpleString4 进入方法执行了"+userName+userId);return "返回的userName="+userName;}/** * 和getSimpleString4 中的却别在于 将参数叠加在一起处理了; * 形成的key是根据userId_userName 两者决定的 * @author khy * @createTime 2020年10月22日下午1:36:35 * @param userId * @param userName * @return */@RequestMapping("/getSimpleString5")@Cacheable(cacheManager="redisCacheManager" ,value="khy",key="#userId+'_'+#userName")public String getSimpleString5(Integer userId,String userName){   
		System.out.println("getSimpleString5 进入方法执行了"+userName+userId);return "返回的userName="+userName;}/** * 这个里面是将请求参数中的 userName 和password 组合起来作为redis中的key * @author khy * @createTime 2020年11月3日上午9:18:14 * @param userEntity * @return */@RequestMapping("/getSimpleEntity4")@Cacheable(cacheManager="redisCacheManager" ,value="user",key="#userEntity.userName+'_'+#userEntity.password")public UserEntity getSimpleEntity4(UserEntity userEntity){   
		UserEntity cacheEntity = new UserEntity();
		cacheEntity.setId(1);
		cacheEntity.setAge(10);
		cacheEntity.setUserName("小康康");
		cacheEntity.setPassword("password123");
		cacheEntity.setCreateTime(new Date());
		System.out.println("getSimpleEntity4 进入方法内容");return cacheEntity;}

5.PrefixKeyController 带有自定义key的

请求通过请求下面的链接在redis中设置的key内容
http://localhost:8080/prefixKey/getSimpleString1?userName=candy
	http://localhost:8080/prefixKey/getSimpleString1?userName=candy1

生成的key 的类型下图 这个是在自定义的前缀生成器中
如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的

/** * 通过prefixKeyGenerator设置对应的前缀内容; * 通过 我们自定义的前缀类来设置对应缓存中key的前缀内容; * http://localhost:8080/prefixKey/getSimpleString1?userName=candy * 然后在对应的缓存中设置的 key是 * prefix:PrefixKeyController.getSimpleString1:candy * @author khy * @createTime 2020年11月3日上午10:02:01 * @param userName * @return */@RequestMapping("/getSimpleString1")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public String getSimpleString1(String userName){   
		 System.out.println("getSimpleString 进入方法执行了"+userName);return "返回的userName="+userName;}
@RequestMapping("/getSimpleString1")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public String getSimpleString1(String userName){   
		 System.out.println("getSimpleString 进入方法执行了"+userName);return "返回的userName="+userName;}/** * 带有多个参数的通过prefixKeyGenerator来拼接key的 * 请求 http://localhost:8080/prefixKey/getSimpleString2?userName=candy&password=123 * redis设置的缓存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [candy,123] * * 请求 http://localhost:8080/prefixKey/getSimpleString2 * redis设置的缓存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [null,null] * * 请求 http://localhost:8080/prefixKey/getSimpleString2?userName=candy * redis设置的缓存 prefix:PrefixKeyController.getSimpleString2:SimpleKey [candy,null] * * @author khy * @createTime 2020年11月3日上午10:17:58 * @param userName * @param password * @return */@RequestMapping("/getSimpleString2")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public String getSimpleString2(String userName,String password){   
		System.out.println("getSimpleString2 进入方法执行了"+userName+"password="+password);return "返回的userName="+userName;}
如果请求参数是一个对象的话 则需要toString()需要重写方法参数对象
/** * 请求参数是指定的对象内容; * 但是如果不对UserEntity 处理的话生成的的 key是带有 * prefix:PrefixKeyController.getSimpleEntity:com.khy.entity.UserEntity@e1475c6 * 内存地址的;这样每次请求都不是同一个是有问题需要将对象的toString()方法重写 * return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); * * 请求的是http://localhost:8080/prefixKey/getSimpleEntity * prefix:PrefixKeyController.getSimpleEntity:UserEntity[userName=<null>,password=<null>,id=<null>,age=<null>,createTime=<null>] * * 请求的是http://localhost:8080/prefixKey/getSimpleEntity?userName=candy&password=123 * prefix:PrefixKeyController.getSimpleEntity:UserEntity[userName=candy,password=123,id=<null>,age=<null>,createTime=<null>] * @author khy * @createTime 2020年11月3日上午10:25:46 * @param userEntity * @return */@RequestMapping("/getSimpleEntity")@Cacheable(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public UserEntity getSimpleEntity(UserEntity userEntity){   
		UserEntity cacheEntity = new UserEntity();
		cacheEntity.setId(1);
		cacheEntity.setAge(10);
		cacheEntity.setUserName("小康康");
		cacheEntity.setPassword("password123");
		cacheEntity.setCreateTime(new Date());
		System.out.println("getSimpleEntity 进入方法内容");return cacheEntity;}
public class UserEntity implements Serializable{   @Overridepublic String toString() {   
		  try {   return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);} catch (Exception var2) {   var2.printStackTrace();return super.toString();}}}

6.@CachePut

不管缓存中是否有值都会将当前返回值更新到缓存中去
/** * @CachePut 注解是不管缓存里面有没有都会执行当前方法并且将结果返回设置到缓存中去 * 每次请求访问 http://localhost:8080/prefixKey/getSimpleEntity2?userName=candy&password=123 * 不管缓存里面是否包含key 都会执行下面的方法并且设置返回值内更新到缓存中 * prefix:PrefixKeyController.getSimpleEntity2:UserEntity[userName=candy,password=123,id=<null>,age=<null>,createTime=<null>] * @author khy * @createTime 2020年11月3日上午10:54:22 * @param userEntity * @return */@RequestMapping("/getSimpleEntity2")@CachePut(cacheManager="redisCacheManager" ,value="khy",keyGenerator="prefixKeyGenerator")public UserEntity getSimpleEntity2(UserEntity userEntity){   
		UserEntity cacheEntity = new UserEntity();
		cacheEntity.setId(1);
		cacheEntity.setAge(10);
		cacheEntity.setUserName("小康康");
		cacheEntity.setPassword("password123");
		cacheEntity.setCreateTime(new Date());
		System.out.println("getSimpleEntity2 进入方法内容");return cacheEntity;}

7.@CacheEvict

执行当前方法会将指定的缓存中的key值全部清空
清空缓存的原理主要是根据 注解属性中的value指定的;
主要观察我们在@Cacheable 注解中指定value 之后会在缓存中生成一个zset结构的缓存,
每当调用方法请求参数不同都会多一对key-value的缓存值同时zset中也会缓存对应的key;
所以清空缓存的原理就是从zset中获取所有要清空缓存的key然后清除之后将zset缓存也清空
/** * 清空缓存中的数据主要和value值有关系,也就是在@Cacheable注解 value属性设置了khy * 则在缓存中生成一个zset格式的记录里面的所有的key; 然后清除的时间是根据zset中到的元素 * 的所有元素进行删除也就是不管该方法userName传递的是不是上面方法传递的都会被清空 * 方法中的value可以指定多个key * @author khy * @createTime 2020年11月18日上午10:34:21 * @param userName * @return */@RequestMapping("/clearSimple")@CacheEvict(cacheManager="redisCacheManager" ,value="khy",allEntries=true)public UserEntity clearSimple(String userName){   
		UserEntity cacheEntity = new UserEntity();
		cacheEntity.setId(1);
		cacheEntity.setAge(10);
		cacheEntity.setUserName("小康康");
		cacheEntity.setPassword("password123");
		cacheEntity.setCreateTime(new Date());
		System.out.println("清空缓存功能方法已执行");return cacheEntity;}

以上就是@Cacheable和相关注解的使用;其实除了觉得比较方便;一般项目中缓存也不会这么使用;都会有同一个工具类生成,而且都是根据我们指定数据结构来生成的.而不是这种都当成key-value来处理的

上述就是小编为大家分享的如何通过Springboot和@Cacheable注解完成方法调用拉去缓存以及@CacheEvict清空缓存的原理是怎样的了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。

推荐阅读:
  1. Android之计算缓存大小并且清空缓存
  2. SDWebimage清空缓存

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring boot @cacheable @cacheevict

上一篇:Ubuntu Server中iptables防火墙怎么用

下一篇:go版本有哪些变化

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》