阅读量:0
本文将采用AOP + 反射 + Redis自定义缓存标签,重构缓存代码,打造基础架构分布式缓存组件
配置
需要在Redis配置类中开启AOP自动代理,即通过==@EnableAspectJAutoProxy ==注解实现该功能
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; 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.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.RedisCacheConfiguration; 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.serializer.*; import java.lang.reflect.Method; import java.time.Duration; /** * Redis配置类 * */ @Configuration @EnableCaching @EnableAspectJAutoProxy // 开启AOP自动代理 public class RedisConfig { @Bean @Primary public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); //String的序列化方式 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 使用GenericJackson2JsonRedisSerializer 替换默认序列化(默认采用的是JDK序列化) GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); //序列号key value redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
自定义注解
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 BaizeRedisCache { // redis的key前缀 String keyPrefix(); // SpringEL表达式,解析占位符对应的匹配value值 String matchValue(); }
定义Redis缓存组件切面类
import jakarta.annotation.Resource; 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.core.DefaultParameterNameDiscoverer; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * @Description:redis缓存组件切面类 * @Author: smart * @Date: 2024/8/7 8:40 */ @Aspect @Component public class BaizeRedisCacheAspect { @Resource private RedisTemplate redisTemplate; // 配置织入点 @Pointcut("@annotation(com.atguigu.daijia.common.config.redis.BaizeRedisCache)") public void cachePoint(){} @Around("cachePoint()") public Object doCache(ProceedingJoinPoint joinPoint){ // TODO Object result = null; try{ // 1、获取重载后的方法名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 2、确定方法名上配置的注解标签 BaizeRedisCache baizeRedisCache = method.getAnnotation(BaizeRedisCache.class); // 3、获得该注解标签上配置的参数进行封装和调用 String keyPrefix = baizeRedisCache.keyPrefix(); String matchValueSpringEL = baizeRedisCache.matchValue(); // 4、SpringEL解析器 SpelExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(matchValueSpringEL); StandardEvaluationContext context = new StandardEvaluationContext(); // 5、获得方法里的形参个数 Object[] args = joinPoint.getArgs(); DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); String[] parameterNames = discoverer.getParameterNames(method); for (int i = 0; i < parameterNames.length; i++) { System.out.println("获得方法里参数名和值: " + parameterNames[i] + "\t" + args[i].toString()); context.setVariable(parameterNames[i], args[i].toString()); } // 6、拼接redis最终key形式 String key = keyPrefix + ":" + expression.getValue(context).toString(); System.out.println("拼接redis的最终key形式: " + key); // 7、先去redis中查询是否存在数据 result = redisTemplate.opsForValue().get(key); if (result != null){ // 若redis存在则直接返回结果,无需到数据库中查询 return result; } // 8、redis中没有则去数据库中查询 result = joinPoint.proceed(); // 9、主业务逻辑查询完之后,将数据存入redis if (result != null){ redisTemplate.opsForValue().set(key, result); } }catch (Throwable throwable){ throwable.printStackTrace(); } return result; } }