1. 需求分析
某公司需要将原有的Redis缓存抽离出来,并且还要要实现:
- 可配置
- 热拔插
- 高可用
- 高通用
请问你会如何实现?
2. 思路
话不多说直接上思路:
- 自定义缓存注解,当容器扫描到该注解自动调用AOP想应的增强方法为原有的业务逻辑赋能
- 使用SpringEL表达式占位符扩展
3. 代码实现
3.1 注解创建
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRedisCatch {
// 前缀
String keyPrefix();
// 匹配的value值
String matchValue();
}
3.2 切面类
@Component
@Aspect
public class MyRedisCacheAspect {
@Resource
private RedisTemplate redisTemplate;
//换成自己注解的全类名
@Around("@annotation(com.example.redisexercises.annotation.MyRedisCatch)")
public Object doCatch(ProceedingJoinPoint joinPoint){
Object res = null;
try {
//获取当前的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
//获取方法上的注解
MyRedisCatch redisCatchAnnotation = method.getAnnotation(MyRedisCatch.class);
//获得注解中的参数
String keyPrefix = redisCatchAnnotation.keyPrefix();
String matchValue = redisCatchAnnotation.matchValue();
//SpringEL解析器
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
Expression expression = spelExpressionParser.parseExpression(matchValue);
EvaluationContext context = new StandardEvaluationContext();
//获得方法中形参
Object[] args = joinPoint.getArgs();
DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = defaultParameterNameDiscoverer.getParameterNames(method);
for(int i = 0;i< parameterNames.length;i++){
context.setVariable(parameterNames[i], args[i].toString());
}
//拼接最终redis key
String key = keyPrefix + ":" + expression.getValue(context).toString();
//查询redis
res = redisTemplate.opsForValue().get(key);
if(res != null){
return res;
}
//没查询到结果则则主业务类查数据库
res = joinPoint.proceed();
//最终回填到redis中,下次查询命中
if(res != null)
redisTemplate.opsForValue().set(key, res, 60, TimeUnit.SECONDS);
}catch (Throwable throwable){
throwable.printStackTrace();
}
return res;
}
}
3.3 在任意方法上标注注解以实现缓存功能
@ApiOperation("获取商品")
@GetMapping("/get/{id}")
@MyRedisCatch(keyPrefix = "user", matchValue = "id")
public Order get(@PathVariable Integer id){
//数据库查找业务逻辑
}