大家好,我是小义。Spring中的@Cacheable注解相信大家都有用过,其key属性是支持SPEL表达式的,像key="#root.args[0]"取到的就是方法第一个入参,这极大地简化了缓存key的配置。
上面提到的SPEL(Spring Expression language),是Spring3.0开始引入的Spring表达式语言,可以通过程序在运行期间执行的表达式将值装配到对象的属性或构造函数中。
小义之前有实现过带过期时间的自定义缓存注解@CacheableTtl,但是在实现key的动态设值功能是通过在方法入参里添加另外一个注解@CacheParam实现的。具体的可以看看之前的文章。虽然@CachePram也支持了SPEL表达式,但在实现过程和使用上还是没有像@Cacheable的可以那样简单快捷。
其实直接用SPEL就够了,只怪自己学艺不精,之前绕了一大圈,下面来看看具体代码实现。
在拦截器中获取拦截方法入参中的参数名称与参数值映射,然后通过解析spel表达式的key,获取真正存入缓存中的key值。
代码语言:javascript复制@Component
@Aspect
public class CacheableAspect {
@Pointcut("@annotation(com.invent.momo.annotation.CacheableTtl)")
public void pointcut() {
}
@Around(value = "pointcut()")
public Object around(final ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();//方法入参对象数组
Method method = MethodSignature.class.cast(pjp.getSignature()).getMethod();//方法实体
CacheableTtl cacheable = method.getAnnotation(CacheableTtl.class);//注解
String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(method);//参数名称
StandardEvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < parameterNames.length; i ) {
context.setVariable(parameterNames[i], args[i]);
}
//解析spel表达式的key,获取真正存入缓存中的key值
String key = parseSpelKey(cacheable, context);
//读取缓存
Object value = getCache(cacheable, key);
if (null == value) {
value = pjp.proceed();//执行方法
//设置缓存
setCache(cacheable, key, value);
}
return value;
}
private String parseSpelKey(CacheableTtl cacheable, StandardEvaluationContext context) {
String keySpel = cacheable.key();
Expression expression = new SpelExpressionParser().parseExpression(keySpel);
String key = expression.getValue(context, String.class);
return key;
}
}
拦截器设置好后,在Controller层就可以像使用@Cacheable的key那样,给自定义注解的key灵活设置SPEL表达式了。
代码语言:javascript复制@RequestMapping("/com")
@RestController
public class ComController {
@CacheableTtl(key = "#demoUser.name #demoUser.job", ttl = 5, ttlTimeUnit = TimeUnit.MINUTES)
@PostMapping("/summer")
public CustomVo summer(DemoUser demoUser) {
CustomVo customVo = new CustomVo();
customVo.setName(demoUser.getName() demoUser.getJob());
return customVo;
}
@CacheableTtl(key = "'spring::' #demoUser.name", ttl = 5, ttlTimeUnit = TimeUnit.MINUTES)
@PostMapping("/spring")
public CustomVo spring(DemoUser demoUser) {
CustomVo customVo = new CustomVo();
customVo.setName("xx");
return customVo;
}
@CacheableTtl(key = "#id #name", ttl = 5, ttlTimeUnit = TimeUnit.MINUTES)
@PostMapping("/winter")
public CustomVo winter(@RequestParam("id") String id, @RequestParam("name") String name) {
CustomVo customVo = new CustomVo();
customVo.setAge("18");
customVo.setName(name);
return customVo;
}
}
上文提到的历史文章如下,感兴趣的同学可以读一读。欢迎关注小义公众号,一起学习新知识。
推荐阅读:
Spring Cache解析
实现Cacheable注解