You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,在获取缓存时传入明确的键值类型:

  1. 实现自定义缓存解析器:
@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));
    }
}
  1. 在缓存注解中指定该解析器:
@Cacheable(value = "serviceHostName", cacheResolver = "typedCacheResolver")
public String getServiceHostName() {
    // 业务逻辑代码
}

内容的提问来源于stack exchange,提问作者Francesca Pizzimenti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 02:04:57