原理解释
友情链接 手写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);
- }
等同于
- @Override
- public 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的的中。
实现方法
- 使用代理模式,在方法执行前和执行后可以添加其他处理程序,本文采用springAOP +注解方式。
- 集成redis,封装Redis工具类
- 原版本不支持 过期时间 设置,本文将实现
源代码
缓存配置类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生成策略
- @Bean
- public KeyGenerator keyGenerator() {
- return new KeyGenerator() {
- @Override
- public 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();
- }
- };
- }
-
- // 缓存管理器
- @Bean
- public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
- RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
- // 设置缓存过期时间
- cacheManager.setDefaultExpiration(10000);
- return cacheManager;
- }
-
- @Bean
- public 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序列化方式采用jackson
- template.setValueSerializer(jackson2JsonRedisSerializer);
- // hash的value序列化方式采用jackson
- template.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.jb51.net/article/233562.htm
准备工作做好之后开始正式编写注解@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 {
- @Autowired
- private RedisUtil redisUtil;
-
- @Pointcut("@annotation(com.huajie.annotation.ExtCacheable)")
- public void annotationPointcut() {
- }
-
- @Around("annotationPointcut()")
- public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
- // 获得当前访问的class
- Class<?> 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的的截图

以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。