Ehcache 两次注册 beans

Ehcache is registering beans two times

我不知道这是否是正常行为,但是当我启动 Spring 服务器时,我检查缓存是否正常工作,此时 Ehcache 会在它正确注册时通知我型号:

2020-11-06 21: 38: 23.253 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.User

我注意到 Ehcache 为我注册了两次模型:

2020-11-06 21: 38: 22.860 INFO 32643 --- [main] c.g.a.s.b.c.MultiCacheManagerConfig: Created cache memory with size (2)
2020-11-06 21: 38: 22.860 INFO 32643 --- [main] c.g.a.s.b.c.MultiCacheManagerConfig: Created cache disk with size (1)
2020-11-06 21: 38: 22.860 INFO 32643 --- [main] c.g.a.s.b.c.MultiCacheManagerConfig: Created cache with size (3)
2020-11-06 21: 38: 23.054 INFO 32643 --- [main] org.ehcache.core.EhcacheManager: Cache 'com.github.apolalca.spring.boot.model.bean.User' created in EhcacheManager.
2020-11-06 21: 38: 23.056 INFO 32643 --- [main] org.ehcache.core.EhcacheManager: Cache 'com.github.apolalca.spring.boot.repository.UserRepository.findByUsername' created in EhcacheManager.
2020-11-06 21: 38: 23.140 INFO 32643 --- [main] org.ehcache.core.EhcacheManager: Cache 'com.github.apolalca.spring.boot.model.bean.Blacklist' created in EhcacheManager.
2020-11-06 21: 38: 23.253 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.User
2020-11-06 21: 38: 23.255 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.repository.UserRepository.findByUsername
2020-11-06 21: 38: 23.255 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.Blacklist
2020-11-06 21: 38: 23.259 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.User
2020-11-06 21: 38: 23.260 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.repository.UserRepository.findByUsername
2020-11-06 21: 38: 23.260 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.Blacklist

我创建了一个监听器,当我们创建或修改用户时跳转(例如),这个确实注册了一个创建,但是从Eh107CacheManager我看到它注册了两次,为什么会这样?

缓存有两种配置(这是我的第一个配置,所以可以肯定,如果失败是我的错),我正在创建两种类型的配置,一种用于用户模型的内存配置,另一种持久的黑名单的类型配置(用于保存到磁盘)(例如)

@Configuration
@EnableCaching
public class MultiCacheManagerConfig extends CachingConfigurerSupport {
    private static final Logger LOG = LoggerFactory.getLogger(MultiCacheManagerConfig.class);

    @Bean
    public Blacklist blacklistBean() {
        return new Blacklist();
    }

    @Bean
    public org.springframework.cache.CacheManager cacheManager() {
        //TODO: In case Multi Cluster -> Create createInMultiClusterCacheManager()
        return new JCacheCacheManager(createInMemoryCacheManager());
    }

    private CacheManager getCacheManager(EhcacheCachingProvider provider, DefaultConfiguration configuration) {
        return provider.getCacheManager(provider.getDefaultURI(), configuration);
    }

    private ClassLoader getClassLoader() {
        return this.getClass().getClassLoader();
    }

    private CacheManager createInMemoryCacheManager() {
        long cacheSize = 100;
        long ttl = 200;

        CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder
                .newEventListenerConfiguration(new CustomCacheEventLogger(), EventType.CREATED, EventType.UPDATED, EventType.EVICTED)
                .unordered().asynchronous();

        org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder
                        .heap(cacheSize))
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ttl)))
                .withService(cacheEventListenerConfiguration)
                .build();

        org.ehcache.config.CacheConfiguration<Object, Object> cacheConfigurationDisk = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder
                        .heap(cacheSize)
                        .disk(20, MemoryUnit.MB, true))
                .withService(cacheEventListenerConfiguration)
                .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ttl)))
                .build();

        Map<String, CacheConfiguration<?, ?>> caches = createCacheConfigurations(cacheConfiguration);
        LOG.info("Created cache memory with size({})", caches.size());
        Map<String, CacheConfiguration<?, ?>> cachesDisk = createCacheConfigurationsDisk(cacheConfigurationDisk);
        LOG.info("Created cache disk with size({})", cachesDisk.size());
        caches.putAll(cachesDisk);
        LOG.info("Created cache with size({})", caches.size());
        EhcacheCachingProvider provider = getCachingProvider();

        DefaultConfiguration configuration = new DefaultConfiguration(caches, getClassLoader(),
                new DefaultPersistenceConfiguration(new File(
                        "{folder}/persistence",
                        "myUserData"
                ))
        );

        return getCacheManager(provider, configuration);
    }



    private Map<String, org.ehcache.config.CacheConfiguration<?, ?>> createCacheConfigurations(org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration) {
        Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = new HashMap<>();
        caches.put(com.github.apolalca.spring.boot.model.bean.User.class.getName(), cacheConfiguration);
        caches.put(UserRepository.USERS_BY_LOGIN_CACHE, cacheConfiguration);
        return caches;
    }

    private Map<String, org.ehcache.config.CacheConfiguration<?, ?>> createCacheConfigurationsDisk(org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration) {
        Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = new HashMap<>();
        caches.put(com.github.apolalca.spring.boot.model.bean.Blacklist.class.getName(), cacheConfiguration);
        return caches;
    }

    private EhcacheCachingProvider getCachingProvider() {
        return (EhcacheCachingProvider) Caching.getCachingProvider();
    }
}

顺便说一下,我使用的是 ehcache 3 和 spring 5 您好,非常感谢!

UPDATE: this problem should be resolved in the (upcoming) version 3.9.7, as per the issue mentioned below. Update your ehcache version or track the version releases in order to have the issue fixed.


经过一些调查,这似乎是因为以下错误: CacheConfiguration 和 CacheStatistics MBean 一次又一次注册

发生的事情是我们想使用 Spring 和 EhCache 3 的组合。

不幸的是,目前 spring 中的 EhCache 3 没有 org.springframework.cache.CacheManager 可用(仅适用于 v2,即 org.springframework.cache.ehcache.EhCacheCacheManager),所以我们不得不回退到使用 JCacheCacheManager.

JCacheCacheManager 也必须在内部初始化其缓存(这些是〜代理缓存),这是通过继承自 InitializingBeanafterPropertiesSet() 方法完成的。调用此 afterPropertiesSet() 后,经过一些间接调用 loadCaches() 方法,其中出现以下代码片段:

    for (String cacheName : cacheManager.getCacheNames()) {
        javax.cache.Cache<Object, Object> jcache = cacheManager.getCache(cacheName);
        caches.add(new JCacheCache(jcache, isAllowNullValues()));
    }

相关部分基本上是这样的:cacheManager.getCacheNames().

从错误报告中可以看出,此方法错误地违反了 no-side-effects 原则并重新初始化了缓存。

TLDR:这是 EHCache v3 中的一个已知错误,并且已经存在一个问题。也许有一天我们会看到修复。我还参考了这个答案对这个问题发表了评论。