问题背景
Spring Cache的键生成。@Cacheable
注解中尝试使用参数名作为键,比如key = "#key"
,但发现缓存键为null,而改用#p0
则可以正常添加key。
问题原因
- Java 编译默认不保留参数名
Java 编译器在编译 .class 文件时,默认不会保留方法的参数名称(出于安全性和优化考虑)。例如:
public String getValue(String key) { ... }
编译后,参数名 key 会被丢弃,变成 arg0。此时,Spring 无法通过 #key 识别参数名。
-
#p0 和 #key 的区别
#p0
是 基于参数位置的引用(第一个参数),不依赖参数名称,总能正确解析。
#key
是 基于参数名称的引用,需要依赖参数名被保留才能生效。如果参数名未保留,SpEL 会认为 key 是一个不存在的变量,导致结果为 null。
Spring Cache的工作原理
当使用@Cacheable
时,Spring会根据指定的SpEL表达式生成缓存键。如果表达式引用了方法参数名,比如#key
,那么Spring需要能够访问到这些参数名。然而,Java在编译时默认不会保留方法参数名称,除非使用-parameters
编译选项。如果参数名不可用,SpEL可能无法解析#key
,导致键为null。而#p0
则是基于参数位置的引用,无论参数名是否存在,都能正确指向第一个参数,因此在这种情况下更可靠。
常见解决方案
- 确保编译时保留参数名,使用
-parameters
选项; - 在IDE中配置保留参数名;
- 使用
@Param
注解显式指定参数名; - 使用位置参数如
#p0
作为替代方案; - 自定义KeyGenerator.
解决
- 启用编译时保留参数名
在编译代码时,强制保留方法的参数名称。这是最彻底的解决方案。
配置方式:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
image.png