Spring Cache的一次问题解决过程
1. 环境
- Spring Boot 2.4.4
- EhCache 3.8.1
2. 背景
想在项目中引入Spring Cache,原先是自己定义注解,利用AOP实现的对方法返回结果的缓存。现在想基于SpringCache重写这些注解。
- 在pom.xml中引入SpringCache相关的依赖:
... 省略更多
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.4.4</version>
</dependency>
... 省略更多
- 在application.properties中加入了以下配置
# cache
spring.cache.type=jcache
spring.cache.jcache.provider=org.ehcache.jsr107.EhcacheCachingProvider
spring.cache.jcache.config=classpath:/ehcache.xml
- 在MainApplication上加上了@EnableCaching
...省略更多
@EnableCaching
public class MainApplication
{
...
}
启动程序的时候,报如下错误:
image.png
3. 补充说明
在《Spring官方文档》中有说明,Ehcache3.x已经完全实现了JSR规范,可以以JCache的方式引入Ehcache。而Spring-context-support-5.3.5.jar中的EhCacheCacheManager使用的是"net.sf.ehcache.CacheManager",这是EhCache 2.x的。
另外的,程序使用的是application.properties配置文件进行配置。Ehchahe及其他Cache在其中该如何配置,格式是怎样的,可以参考“org.springframework.boot.autoconfigure.cache.CacheProperties”这个类。
image.png
4.问题排查分析
在排查问题之前,首先需要大概了解一下Spring Cache的逻辑过程和主要类。其相关资料可以自行百度(可以参考:https://blog.csdn.net/xing_hung/article/details/122753476)。要理解这个过程,关键是理解CacheAutoConfiguration的CacheConfigurationImportSelector,从字面理解就可以知道,其主要功能是“导入”和“选择”。
image.png另外一个是CacheConfigurations。
image.png
4.1 明确问题
从前面的异常消息可以知道,问题是没有找到与jcache对应的CacheManager。现在需要找到CacheManager没有按照配置要求自动构建出来的原因。在JCacheCacheConfiguration中有构建和注册CacheManager的方法,为什么没有调用?
image.png
4.2 方法
JCacheCacheConfiguration这个类上面有很多的注解,特别是@ConditionXXX注解,我们知道它是起条件判定作用的,只有当条件满足时,这个Configuration类里面的@Bean注解才可能起作用。
理解一下注解:
// 这是一个起配置作用的类
@Configuration(proxyBeanMethods = false)
// 当Caching.class和JCacheCacheManager.class这几个类在类路径上,能被ClassLoader找到时此类才起作用。
@ConditionalOnClass({ Caching.class, JCacheCacheManager.class })
// 当找不到CacheManager类型的Bean时此类才起作用
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
// CacheCondition和JCacheAvailableCondition条件成立
@Conditional({ CacheCondition.class, JCacheCacheConfiguration.JCacheAvailableCondition.class })
@Import(HazelcastJCacheCustomizationConfiguration.class)
class JCacheCacheConfiguration implements BeanClassLoaderAware {
... 省略更多
}
这其中有些条件笔者也不能拿捏的准是不是成立,只能借助调试,进一步判定。
-
调试步骤1:
image.png -
调试步骤2,接着一步步走下去,重点关注对org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration的处理。走到这一步发现明显不符合预期,className不为null,但filter之后返回true和className==null走相同的代码,等同于className没有起作用。所以接下来重点关注filter.test方法中的逻辑。
image.png -
调试步骤3。从方法名称上就可以看出,当返回true时,将被排除。即Collection经filter之后,如果没有元素了,那么就将排除。
image.png -
调试步骤4。关注为什么result会变空。
image.png -
调试步骤5。发现是因为找不到类javax.cache.Caching。
image.png
5. 解决办法
在pom.xml中引入javax.cache,问题得到解决。
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
6. 事后总结。
这个问题其实应该可以更早的就想到。因为从官方文档
image.png
或者是application.properties
image.png
都可以看到“JSR-107”。JSR,是Java Specification Requests的缩写,意思是Java规范提案,是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。它很可能是没有纳入到JDK中的,需要另外下载相应的javax包。