在同一虚拟机中已经存在另一个未命名的CacheManager(ehCache 2.5)

81

运行我的JUnit测试时会发生什么...

Another CacheManager with same name 'cacheManager' already exists in the same VM. Please 
provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same
   CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.

The source of the existing CacheManager is: 
 DefaultConfigurationSource [ ehcache.xml or ehcache-failsafe.xml ]

这个异常的原因是什么?可能会同时运行多个cacheManager吗?

这是我使用Spring 3.1.1配置cacheManager的方式。它明确将cacheManager的作用域设置为“singleton”。

<ehcache:annotation-driven />

<bean
    id="cacheManager"
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
    scope="singleton"
    />

ehcache.xml文件如下

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
     updateCheck="false"
     maxBytesLocalHeap="100M" 
     name="cacheManager"
     >
 ....
 </ehcache>

最后我的类

@Component
public class BookingCache implements CacheWrapper<String, BookingUIBean> {

     @Autowired
     private CacheManager ehCacheManager;
      ....
}

我非常确定在我的代码库中只涉及一个cacheManager。可能是其他东西正在运行第n个实例。


4
我已经看到了使用 ehCache 2.5 及更高版本时出现的相同问题。使用 2.4.7 版本不会引起此问题,但是了解如何使 2.5 版本与 junit 兼容将是有益的。 - aweigold
1
谢谢。我已经切换回2.4.7版本,目前还可以。此外,有一篇博客文章讨论了可能的解决方法(尽管它们都不是很理想)https://norrisshelton.wordpress.com/2012/03/08/spring-3-1-caching-abstraction-with-ehcache/。 - simou
Norris Shelton的解决方案对我有效 (https://norrisshelton.wordpress.com/2012/03/08/spring-3-1-caching-abstraction-with-ehcache/) - jbbarquero
这个解决方案似乎对我不起作用,虽然我正在使用testNG。我仍然收到“同一VM中已经存在具有相同名称'myCacheManager'的另一个CacheManager”的错误消息 :( - nodje
我相信这里同样解决了这个问题。 - Lekkie
18个回答

46
您的EhCacheManagerFactoryBean可能是单例的,但它正在构建多个CacheManagers并尝试给它们相同的名称。这违反了Ehcache 2.5 语义
引用:
Ehcache 2.5之前的版本允许在JVM中存在具有相同名称(相同配置资源)的任意数量的CacheManagers。
Ehcache 2.5及更高版本不允许在同一JVM中存在具有相同名称的多个CacheManagers。创建非单例CacheManagers的CacheManager()构造函数可能会违反此规则。
通过将shared属性设置为true,告诉工厂bean在JVM中创建共享的CacheManager实例。
<bean id="cacheManager"
      class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
      p:shared="true"/>

5
需要注意的一点是,如果Spring正在自动代理EhCacheManagerFatoryBean,即使您将p:shared设置为true,仍可能会出现错误。解决方法是不使用文件的默认名称,而是将ehcache.xml重命名并在EhCacheManagerFactoryBean声明中添加p:config-location以及新文件名。 - TechTrip
1
@TechTrip:我已经完全这样做了:我在我的EhCacheManagerFactoryBean上使用了p:config-location="classpath:ehcache-foo.xml" p:shared="true",但是由于我的JVM上有两个WAR使用了相同的模块,所以我仍然遇到了冲突。 - mcv
3
如果我理解有误,请纠正我,这是否意味着第二个测试会重用第一个测试中的缓存,并保留其中任何已缓存的数据?这可能会导致难以调试的行为,因为第二个测试不是从已知状态开始。测试结果可能取决于测试顺序。(当然,黑盒测试不应该依赖于是否使用缓存,但是依赖于缓存状态的性能测试是完全有效的,或者您正在测试使用ehcache的自己的缓存工具。) - Henno Vermeulen

44

我在使用 JPA (2.0) + Hibernate (3.6.4) + Spring (3.2.4) 进行集成测试时遇到了同样的问题。

通过以下 Hibernate 配置解决了此问题:

<property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory"/>

使用时可以考虑改用

<property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.EhCacheRegionFactory"/>

3
我使用了这个解决方案来修复我的Spring Boot测试问题: 我使用的是org.hibernate.cache.ehcache.EhCacheRegionFactory而不是Hibernate 4的net.sf.ehcache.hibernate.EhCacheRegionFactory。 在Spring Boot中,你可以在application.properties文件中设置它: spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory, 或者如果你想将配置限制在测试中,可以使用@TestPropertySource。 - Michael Koch
1
升级到Hibernate 5.2后,太棒了,救了我的一天。 - smallufo

24
你的问题涉及Spring测试框架中的上下文加载优化。Spring(默认情况下)不会在测试类完成后销毁上下文,希望另一个测试类可以重用它(而不是从头开始创建)。您可以使用@DirtiesContext覆盖此默认设置,或者如果您使用maven,则可以将surefire forkMode设置为“always”并为每个测试类创建一个新的VM。

3
不改变实际运行配置,干净地解决了测试环境的问题。不错! - lost
2
forkmode=always是一个不错的选择,但已经过时了。请参见:http://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html尝试使用forkCount = 1(默认值),reuseForks = false。 - theINtoy

14

升级到Hibernate 5之后,我必须使用:

<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>

改为:

<property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory"/>
请注意各个包裹之间的差异。

12

太遗憾了,我只读了这里的第一个答案,之后我回到Eclipse中阅读所有Spring和Ehcache初始化源代码,才发现需要name属性,否则配置文件将被忽略... - jpprade
好的解决方案,我在两个不同的模块中使用了ehcache,在每个模块中都有自己的文件,导致出现了相同的异常,但这个解决方案解决了这个问题。 - Henno Vermeulen
1
这个解决方案仅适用于存在冲突配置(多个ehcache.xml文件)的情况,如果您只使用一个,则可能是另一个问题。 - Michael Holst
@MichaelHolst 确实,我认为还有另一个问题,因为这个问题对我来说又出现了。我猜我在某个地方忘记了 @ DirtiesContext。 - Henno Vermeulen
通常在单个VM中,您应该只初始化缓存实例一次,但是对于测试,可以销毁它,然后重新创建它。我(第二次)遇到这个错误的情况是由于我的应用程序中的第二个线程在第一个线程销毁它之前初始化了缓存。 - Henno Vermeulen
显示剩余2条评论

6
为了后代:更好的方法是使用EhCacheManagerFactoryBean的“accept-existing”属性。这个属性可以帮助你更好地处理缓存。

4

EhCacheManagerFactoryBean#shared设置为true对我有用。

EhCacheManagerFactoryBean#acceptExisting设置为true对我无效。

import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class EhCacheConfiguration {

    @Bean
    public EhCacheCacheManager ehCacheCacheManager() {

        return new EhCacheCacheManager(ehCacheManagerFactoryBean().getObject());
    }


    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {

        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();

        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);

        return cacheManagerFactoryBean;
    }
}

正如在使用Spring 4中不需要XML的EhCache中所解释的那样。


2
当我切换到Spring Boot 2.0.2时,我遇到了这个问题。通过以下方法解决:
在application.yml中删除以下内容:
spring.jpa.properties.hibernate.cache.region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory

pom.xml中的REMOVE

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

只在pom.xml中保留

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
</dependency>

2
我通过在resources.groovy中添加以下内容解决了这个问题:
beans = { ... aclCacheManager(EhCacheManagerFactoryBean) { shared = true } ... }

2

如果你只是测试业务服务而不需要使用二级缓存,可以在Spring配置文件中删除二级缓存配置,这样你的测试将会成功运行。以下是我的二级缓存配置:

 <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="defaultPU" />
        <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">false</prop>
                <!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.cache.use_query_cache">false</prop>
            </props>
        </property>
    </bean>

如果我更改为二级缓存配置的完整配置,实际的Web应用程序在运行时使用,就像这样:

    <bean id="entityManagerFactory"
            class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="persistenceUnitName" value="defaultPU" />
            <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
            <property name="jpaProperties">
                <props>
                    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                    <prop key="hibernate.show_sql">false</prop>
                    <!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
                    <prop key="hibernate.cache.use_second_level_cache">true</prop>
                    <prop key="hibernate.cache.use_query_cache">true</prop>
                    <prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop>               
                    <prop key="net.sf.ehcache.configurationResourceName">ehcache/ehcache-hibernate-local.xml</prop>
                </props>
            </property>
        </bean>

然后我遇到了相同的异常:“另一个未命名的CacheManager已经存在于同一VM中”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接