SpringBoot的Cacheable缓存问题一则
Posted 北亮bl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot的Cacheable缓存问题一则相关的知识,希望对你有一定的参考价值。
前几个月在公司的老项目里,封装了一个统一缓存,使用Cacheable注解方案,并安排上线了。
这周增加了一个缓存命中率的统计数据,发现有些场景的缓存命中率为0,太奇怪了。
1、在本地调试了一下,可以重现。
出问题的代码如下:
@FeignClient(url = xxx)
public interface XxxService
@PostMapping("/refresh/checkVpsStatus")
@Cacheable(value = "aaa", key = "'vpsStatus:' + #instanceId")
ResponseData checkVpsStatus(String instanceId);
其它的代码,都是class,而不是interface,功能都正常,也就是只有interface才有问题。
2、接着做代码跟踪,跟到CacheAspectSupport.generateKey
方法时,发现
Object key = context.generateKey(result);
得到的key为 vpsStatus:null
也就是问题出现没获取到调用的形参instanceId
的值,从而读取缓存失败了。
3、进一步跟踪,解析spel表达式过程都正常,最后发现,在获取Method的形参参数名时出了问题,
在类PrioritizedParameterNameDiscoverer
里有一段代码:
public String[] getParameterNames(Method method)
for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers)
String[] result = pnd.getParameterNames(method);
if (result != null)
return result;
return null;
跟踪到这里时,this.parameterNameDiscoverers
有2个实例,分别是:
- StandardReflectionParameterNameDiscoverer
- LocalVariableTableParameterNameDiscoverer
这2个类实例,都是去读取Method的参数名的,但是都返回null,取不到。
4、接着用class这种能正常读取缓存的代码,验证了一下,发现
LocalVariableTableParameterNameDiscoverer
里能正常读取到class.Method的形参参数名
用 cacheable
+ LocalVariableTableParameterNameDiscoverer
搜索了一下,
说是要在Javac增加 -parameters 编译参数就可以读取到接口的形参名
于是在Idea里,配置了一下编译参数,再运行项目,果然OK了:
5、截止这里,问题清楚了,编译时,默认会擦除interface的形参名,
难道要我去配置Jenkins的构建参数吗?
去官网查了一下
https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/
发现那边明明白白写着,spring-boot已经支持了 -parameters 参数了:
3. Using the Plugin
Maven users can inherit from the spring-boot-starter-parent project to obtain sensible defaults. The parent project provides the following features:
Java 1.8 as the default compiler level.
UTF-8 source encoding.
Compilation with -parameters.
我这个老项目也是SpringBoot的啊,为啥会不行呢?
于是新建了一个项目,工作都是正常的啊。
接着比对新建项目和老工程,发现了差异点:
老项目居然只是在<dependencies>
里引用了spring-cloud相关库,而没有通过<parent>
去继承
6、最终解决,在老项目的pom.xml
里,加上继承关系就好了
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
补充:
即使没有继承关系,也可以通过索引方式访问方法的参数,参考官网说明:
Method arguments can be accessed by index. For instance the second argument can be accessed via #root.args[1],
#p1 or #a1. Arguments can also be accessed by name if that information is available.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html
以上是关于SpringBoot的Cacheable缓存问题一则的主要内容,如果未能解决你的问题,请参考以下文章
Springboot集成Redis详细教程(缓存注解使用@Cacheable,@CacheEvict,@CachePut)