Spring Ehcache3指定键值类型抛出异常,无XML配置下如何解决?
问题:Spring Boot整合Ehcache3指定键值类型启动报错(非XML配置)
与Stack Overflow上《Spring Ehcache3 cause exception with key-type and value-type》问题场景一致,区别在于未使用XML配置,而是通过Java配置类CacheConfig实现缓存配置:
@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { // 创建缓存配置 CacheConfiguration<String, String> cacheConfiguration = CacheConfigurationBuilder .newCacheConfigurationBuilder( String.class, String.class, ResourcePoolsBuilder.heap(1000).build() ).build(); // 获取缓存管理器 CachingProvider cachingProvider = Caching.getCachingProvider(); CacheManager cacheManager = cachingProvider.getCacheManager(); // 转换为Spring Boot可识别的JSR-107配置 javax.cache.configuration.Configuration<String, String> configuration = Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration); // 创建指定缓存 cacheManager.createCache("serviceHostName", configuration); // 添加JVM关闭钩子以关闭缓存管理器 Runtime.getRuntime().addShutdownHook(new Thread(cacheManager::close)); // 返回Bean return cacheManager; } }
部署项目时抛出如下异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheManager' defined in class path resource [org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.class]: Cache [serviceHostName] specifies key/value types. Use getCache(String, Class, Class).... Caused by: java.lang.IllegalArgumentException: Cache [serviceHostName] specifies key/value types. Use getCache(String, Class, Class) at org.ehcache.jsr107.Eh107CacheManager.getCache(Eh107CacheManager.java:271) ~[ehcache-3.0.0.jar:3.0.0 6973aca181d2b3e0010945a89933f3c99f4460c5]....
解决方案
方法一:用Spring的JCacheCacheManager包装JSR-107缓存管理器
Spring默认调用无类型参数的getCache(String)方法,而Ehcache3指定键值类型后要求使用带类型参数的getCache(String, Class, Class)。将JSR-107的CacheManager包装为Spring的JCacheCacheManager,让它适配带类型的缓存获取逻辑:
修改后的配置类代码:
@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { // 创建缓存配置 CacheConfiguration<String, String> cacheConfiguration = CacheConfigurationBuilder .newCacheConfigurationBuilder( String.class, String.class, ResourcePoolsBuilder.heap(1000).build() ).build(); // 获取JSR-107标准的缓存管理器 CachingProvider cachingProvider = Caching.getCachingProvider(); javax.cache.CacheManager jsr107CacheManager = cachingProvider.getCacheManager(); // 转换为JSR-107兼容配置并创建缓存 javax.cache.configuration.Configuration<String, String> configuration = Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration); jsr107CacheManager.createCache("serviceHostName", configuration); // 添加JVM关闭钩子,确保缓存管理器正常关闭 Runtime.getRuntime().addShutdownHook(new Thread(jsr107CacheManager::close)); // 包装为Spring的JCacheCacheManager,适配带类型的缓存获取 JCacheCacheManager springCacheManager = new JCacheCacheManager(); springCacheManager.setCacheManager(jsr107CacheManager); // 开启严格模式,让Spring使用带类型参数的getCache方法 springCacheManager.setStrict(true); return springCacheManager; } }
方法二:移除缓存的类型约束(仅当不需要类型安全时使用)
如果业务场景不要求严格的键值类型校验,可以将缓存配置的键值类型改为Object.class,这样Spring调用无类型的getCache方法就不会触发报错:
@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { // 不指定具体类型,改用Object.class CacheConfiguration<Object, Object> cacheConfiguration = CacheConfigurationBuilder .newCacheConfigurationBuilder( Object.class, Object.class, ResourcePoolsBuilder.heap(1000).build() ).build(); CachingProvider cachingProvider = Caching.getCachingProvider(); javax.cache.CacheManager jsr107CacheManager = cachingProvider.getCacheManager(); javax.cache.configuration.Configuration<Object, Object> configuration = Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration); jsr107CacheManager.createCache("serviceHostName", configuration); Runtime.getRuntime().addShutdownHook(new Thread(jsr107CacheManager::close)); return new JCacheCacheManager(jsr107CacheManager); } }
方法三:自定义缓存解析器,明确指定类型获取缓存
如果需要针对特定缓存精细控制类型,可以自定义CacheResolver,在获取缓存时传入明确的键值类型:
- 实现自定义缓存解析器:
@Component public class TypedCacheResolver implements CacheResolver { @Autowired private javax.cache.CacheManager jsr107CacheManager; @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) { // 根据缓存名称获取带类型的缓存,这里以String键值为例,可根据实际业务调整类型 javax.cache.Cache<String, String> jsrCache = jsr107CacheManager.getCache("serviceHostName", String.class, String.class); return Collections.singleton(new JCacheCache(jsrCache)); } }
- 在缓存注解中指定该解析器:
@Cacheable(value = "serviceHostName", cacheResolver = "typedCacheResolver") public String getServiceHostName() { // 业务逻辑代码 }
内容的提问来源于stack exchange,提问作者Francesca Pizzimenti
相关产品推荐
相关产品推荐

