• SpringBoot的Cacheable缓存问题一则


    前几个月在公司的老项目里,封装了一个统一缓存,使用Cacheable注解方案,并安排上线了。
    这周增加了一个缓存命中率的统计数据,发现有些场景的缓存命中率为0,太奇怪了。

    1、在本地调试了一下,可以重现。
    出问题的代码如下:

    @FeignClient(url = xxx)
    public interface XxxService {
        @PostMapping("/refresh/checkVpsStatus")
        @Cacheable(value = "aaa", key = "'vpsStatus:' + #instanceId")
        ResponseData checkVpsStatus(String instanceId);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其它的代码,都是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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    跟踪到这里时,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.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我这个老项目也是SpringBoot的啊,为啥会不行呢?
    于是新建了一个项目,工作都是正常的啊。
    接着比对新建项目和老工程,发现了差异点:
    老项目居然只是在里引用了spring-cloud相关库,而没有通过去继承

    6、最终解决,在老项目的pom.xml里,加上继承关系就好了

    
        org.springframework.boot
        spring-boot-starter-parent
        2.5.12
         
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    补充:
    即使没有继承关系,也可以通过索引方式访问方法的参数,参考官网说明:

    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.
    
    • 1
    • 2

    https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html

  • 相关阅读:
    Flutter 使用Screen保持屏幕常亮不息屏
    基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(六)
    简单有效的记录日常收支
    maven推送本地jar包到nexus仓库遇到的问题
    王学岗——钉钉视频会议实战,从零手写音视频会议项目
    【JavaEE】多线程案例-定时器
    TVS二极管的小知识
    MYSQL 双表联合查询,通过A表数组查询B表对应的数据
    【第66篇】深度学习在视频多目标跟踪中的应用综述
    使用 Grafana 使用JSON API 请求本地接口 报错 bad gateway(502)解决
  • 原文地址:https://blog.csdn.net/youbl/article/details/126549858