本文将采用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;
}
}