• 原理解释

友情链接  手写redis @ Cacheable注解参数java对象作为键值

@Cacheable注解作用,将带有该注解方法的返回值存放到redis的的中;

使用方法在方法上使用@Cacheable(键=“测试+#P0 + P1#...”)

表示键值为测试+方法第一个参数+方法第二个参数,值为该方法的返回值。

以下源代码表示获取人员列表,Redis的中存放的关键值为'领袖'+ leaderGroupId + UUID + yearDetailId

        @Override@Cacheable(key="'leader'+#p0+#p1+#p2",value="leader")public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {return sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);}

等同于

        @Overridepublic List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {String key = "leader" + leaderGroupId + uuid + yearDetailId;// 判断缓存是否存在redis中boolean hasKey = redisUtil.hasKey(key);if (hasKey) {//如果存在 返还redis中的值Object leadersList = redisUtil.get(key);return (List<Leader>) leadersList;} else {List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);//将查询结果存放在redis中redisUtil.set(key, leadersQuotaDetailList);return leadersQuotaDetailList;}}

说白了就是在原方法的前面判断的关键值是否存在的Redis的中,如果存在就取内存中的值,如果不存在就查询数据库,将查询结果存放在Redis的的中。

  • 实现方法

  1. 使用代理模式,在方法执行前和执行后可以添加其他处理程序,本文采用springAOP +注解方式。
  2. 集成redis,封装Redis工具类
  3. 原版本不支持 过期时间 设置,本文将实现
  • 源代码

缓存配置类RedisConfig

package com.huajie.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;/*** Redis缓存配置类*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.timeout}")private int timeout;// 自定义缓存key生成策略@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, java.lang.reflect.Method method, Object... params) {StringBuffer sb = new StringBuffer();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();}};}// 缓存管理器@Beanpublic CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);// 设置缓存过期时间cacheManager.setDefaultExpiration(10000);return cacheManager;}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();template.setConnectionFactory(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringRedisSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringRedisSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}private void setSerializer(StringRedisTemplate template) {@SuppressWarnings({ "rawtypes", "unchecked" })Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setValueSerializer(jackson2JsonRedisSerializer);}
}

Redis的依赖引入,配置文件,工具类RedisUtil,网上几个版本都类似,本文参考以下版本传送门

https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html

准备工作做好之后开始正式编写注解@Cacheable nextkey()用做二级缓存本文中不会用到

nextKey用法详情> 设计模式(实战) - 责任链模式 <

创建的Java的注解@ExtCacheable

package com.huajie.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtCacheable {String key() default "";String nextKey() default "";int expireTime() default 1800;//30分钟}

SpringAop切面CacheableAspect

package com.huajie.aspect;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.huajie.annotation.ExtCacheable;
import com.huajie.utils.RedisUtil;/*** redis缓存处理* 不适用与内部方法调用(this.)或者private*/
@Component
@Aspect
public class CacheableAspect {@Autowiredprivate RedisUtil redisUtil;@Pointcut("@annotation(com.huajie.annotation.ExtCacheable)")public void annotationPointcut() {}@Around("annotationPointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {// 获得当前访问的classClass<?> className = joinPoint.getTarget().getClass();// 获得访问的方法名String methodName = joinPoint.getSignature().getName();// 得到方法的参数的类型Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();Object[] args = joinPoint.getArgs();String key = "";int expireTime = 1800;try {// 得到访问的方法对象Method method = className.getMethod(methodName, argClass);method.setAccessible(true);// 判断是否存在@ExtCacheable注解if (method.isAnnotationPresent(ExtCacheable.class)) {ExtCacheable annotation = method.getAnnotation(ExtCacheable.class);key = getRedisKey(args,annotation);expireTime = getExpireTime(annotation);}} catch (Exception e) {throw new RuntimeException("redis缓存注解参数异常", e);}// 获取缓存是否存在boolean hasKey = redisUtil.hasKey(key);if (hasKey) {return redisUtil.get(key);} else {//执行原方法(java反射执行method获取结果)Object res = joinPoint.proceed();//设置缓存redisUtil.set(key, res);//设置过期时间redisUtil.expire(key, expireTime);return res;}}private int getExpireTime(ExtCacheable annotation) {return annotation.expireTime();}private String getRedisKey(Object[] args,ExtCacheable annotation) {String primalKey = annotation.key();//获取#p0...集合List<String> keyList = getKeyParsList(primalKey);for (String keyName : keyList) {int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));Object parValue = args[keyIndex];primalKey = primalKey.replace(keyName, String.valueOf(parValue));}return primalKey.replace("+","").replace("'","");}// 获取key中#p0中的参数名称private static List<String> getKeyParsList(String key) {List<String> ListPar = new ArrayList<String>();if (key.indexOf("#") >= 0) {int plusIndex = key.substring(key.indexOf("#")).indexOf("+");int indexNext = 0;String parName = "";int indexPre = key.indexOf("#");if(plusIndex>0){indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");parName = key.substring(indexPre, indexNext);}else{parName = key.substring(indexPre);}ListPar.add(parName.trim());key = key.substring(indexNext + 1);if (key.indexOf("#") >= 0) {ListPar.addAll(getKeyParsList(key));}}return ListPar;}}

业务模块使用方法

@Override@ExtCacheable(key = "Leaders+#p0+#p1+#p2")// 手机端获取领导人员列表public List<Leader> listLeaders(String leaderGroupId, String uuid, String yearDetailId) {List<Leader> leadersQuotaDetailList = sysIndexMapper.listLeaders(leaderGroupId, uuid, yearDetailId);return leadersQuotaDetailList;}

业务模块过期时间使用方法,5分钟过期

@Override@ExtCacheable(key = "mobileCacheFlag", expireTime = 60 * 5)public int cacheFlag() {int mobileCacheFlag = 1;mobileCacheFlag = sysIndexMapper.cacheFlag();return mobileCacheFlag;}

Redis的的截图

手写redis@Cacheable注解 支持过期时间设置相关推荐

  1. redis setnx 分布式锁_手写Redis分布式锁

    分布式锁使用场景 现在的系统都是集群部署,每个服务都不是单节点的了.比如库存服务,可能部署到3台机器上分别命名为节点1,节点2,节点3.库存服务需要扣减库存,扣减库存肯定需要锁吧,如果使用Lock或者 ...

  2. node怎么把token放到redis_从零开始手写 redis(八)朴素 LRU 淘汰算法性能优化

    前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...

  3. Redis[5] key的过期时间删除策略、实现lru算法、持久化配置

    文章目录 Redis[5] key的过期时间删除策略.持久化配置 **Redis6的key过期时间删除策略** Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,服务 ...

  4. hashmap remove 没释放内存_java从零开始手写 redis(13)HashMap 源码原理详解

    为什么学习 HashMap 源码? 作为一名 java 开发,基本上最常用的数据结构就是 HashMap 和 List,jdk 的 HashMap 设计还是非常值得深入学习的. 无论是在面试还是工作中 ...

  5. Redis中key的过期时间

    Redis中key的过期时间 背景 实际项目开发中,我们经常会用到redis进行缓存存储,那么redist中key设置与不设置缓存时间,会给我们日常开发中带来哪些影响呢? 场景1:设置了key但是没有 ...

  6. kafka消息过期时间设置(全局和特定topic)

    文章目录 一.kafka 全局消息过期时间设置 1. 配置文件夹 2. 修改配置 3. 重启配置生效 二.针对特定topic设置过期时间 2.1. 配置文件夹 2.2. 执行设置命令 三.kafka过 ...

  7. vue-cookies过期时间设置无效

    vue-cookies过期时间设置无效 最近项目用到了vue-cookies,用是挺好用的,但是在设置过期时间时让我傻了眼,因为我按照之前网上的设置过期时间的方法,完全没用,不管怎么样设置他都是会话内 ...

  8. redis查看key的过期时间_面试官:你在Redis中设置过带过期时间的Key吗?

    点击上方小伟后端笔记关注公众号 每天阅读Java干货文章 熟悉Redis的同学应该知道,Redis的每个Key都可以设置一个过期时间,当达到过期时间的时候,这个key就会被自动删除. 在为key设置过 ...

  9. 不需要再手写 onSaveInstanceState 了,因为你的时间非常值钱

    如果你是一个有经验的 Android 程序员,那么你肯定手写过许多 onSaveInstanceState 以及 onRestoreInstanceState 方法用来保持 Activity 的状态, ...

最新文章

  1. java开发用amd处理器_HBase1.x实战:协处理器Java开发实例--ObserverCoprocessor
  2. 如何实现高容量大并发数据库服务 | 数据库分布式架构设计
  3. 【鬼网络】之NFS共享服务
  4. cocos2d-html5 简易 下拉表单 控件
  5. 第十二届蓝桥杯青少年组国赛C++中级组 第1题 -- 第3题(python3实现)
  6. Mixed Content: The page at ‘https://XXX’ was loaded over HTTPS, but requested an insecure...........
  7. access 跳过一次for循环_Java中的循环结构
  8. 【人脸识别】基于matlab GUI LBP人脸识别【含Matlab源码 1282期】
  9. keyshot卡住了还能保存吗_Sketchup建模和渲染能取代3dsMax吗?
  10. Android Binder学习(四)之addService流程分析
  11. 大学物理第13章·热力学基础
  12. 恢复和去除时间(Recovery and Removal Time)
  13. 小灰,你出书花了多少钱?
  14. switch怎么切换服务器账号,怎么查看switch账号所属服务器
  15. 【web前端】小人行走
  16. html5鼠标移过切换图片,鼠标移动到图片上切换到另一张图片,移出时又切默认图片...
  17. LED光源的种类与LED光源与灯具的定义介绍
  18. CodeForces 868A Bark to Unlock
  19. 鸿蒙系统2.0电视版,鸿蒙2.0系统,鸿蒙2.0发布系统官方最新版预约 v1.0-手游汇
  20. javascript加入收藏夹 设置首页

热门文章

  1. 洛谷P2245 星际导航
  2. c语言rewind函数作用,C 文件 rewind() 函数
  3. IT、TT、TN系统,你真的了解吗?
  4. 【Linux】C++后台开发面试
  5. 国产分布式数据库在证券行业的应用及实践
  6. PMP考试有哪些科目
  7. 在 Azure ML 上用 .NET 跑机器学习
  8. linux防火墙更改端口号,Linux防火墙开放某端口号
  9. android获取到电信的手机号码,Android基站信息获取以及Sim卡相关信息获取
  10. iOS内存扫描工具实现