1. 程式人生 > >Spring boot熱部署導致CacheManager重名的解決辦法

Spring boot熱部署導致CacheManager重名的解決辦法

前言

最近博主在搭建Spring boot框架,但是有點小問題。由於pom.xml中添加了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
熱部署依賴,導致修改控制層或業務層程式碼之後,應用重啟。但是重啟過程失敗,一直報錯快取重複。報錯如下:

Caused by
: net.sf.ehcache.CacheException: Another CacheManager with same name 'es' 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 ] at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:460
) at net.sf.ehcache.CacheManager.init(CacheManager.java:349) at net.sf.ehcache.CacheManager.<init>(CacheManager.java:237) at org.springframework.cache.ehcache.EhCacheManagerUtils.buildCacheManager(EhCacheManagerUtils.java:54) at org.springframework.cache.ehcache.EhCacheCacheManager.afterPropertiesSet(EhCacheCacheManager.java:74) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ... 109 common frames omitted

前面還有許多其他錯誤,都是由於定義CacheManager 這個bean導致其他定義的bean加載出錯。
PS:
1、ehcache.xml這個檔案除了我自己建立的以外,依賴的shiro-ehcache-1.4.0.jar中也有。
2、ehcache-failsafe.xml這個檔案在依賴的ehcache-core-2.5.0.jar中(我未建立)。

解決方法

在網上找了許多相應的辦法,有說和ehcache-core包版本在2.5.0以後有關,也有說是沒有setShared(true)有關,也有說和isSingleton有關。博主都改過來了,但是都不起作用。昨天偶然發現可能與熱部署有關,終於找到一篇相關的博文下面別人的評論,連結如下:熱部署快取出錯看評論!他的解決方案如下:

這個問題解決了!按如下方法:
1、在ShiroConfiguration中新增如下bean,用它來管理EhCacheManager的生命週期
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
logger.info("ShiroConfiguration--lifecycleBeanPostProcessor");
return new LifecycleBeanPostProcessor();
}
2、在EhCacheManager 上新增如下資訊:@DependsOn("lifecycleBeanPostProcessor")

@Bean
@DependsOn("lifecycleBeanPostProcessor")
public EhCacheManager shiroEhcacheManager() {
logger.info("ShiroConfiguration--ehCacheManager");

EhCacheManager shiroEhcacheManager = new EhCacheManager();

shiroEhcacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");

return shiroEhcacheManager;
}

但是我測試並不成功!!!所以又去看了其他的部落格,差不多把相關的都看了,最終發現其實我漏掉了以前看的一種方法,連結如下:請看第二種方法!由於我是SSM框架轉Spring boot,以前都是xml的配置檔案,現在改為由class定義bean,重啟應用的時候可能不會自動銷燬已存在的CacheManager,需要在定義bean的方法中判斷一下。我和上圖連結中的有點不一樣,有好幾個cache的bean,我原來的如下(錯誤):

@Bean(name = "ehcacheManager")
    public CacheManager ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean bean=new EhCacheManagerFactoryBean();
         //新增XML目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setConfigLocation(resolver.getResource("classpath:ehcache.xml"));
            bean.setShared(true);
            bean.isSingleton();
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Bean(name = "springCacheManager")
    public EhCacheCacheManager cacheCacheManagerBean() {
        EhCacheCacheManager bean=new EhCacheCacheManager();
        bean.setCacheManager(ehCacheManagerFactoryBean());
        return bean;
    }

    @Bean(name = "cacheManager")
    public SpringCacheManagerWrapper springCacheManagerWrapperBean() {
        SpringCacheManagerWrapper bean=new SpringCacheManagerWrapper();
        bean.setCacheManager(cacheCacheManagerBean());
        return bean;
    }

正確的方法是將@Bean(name = “ehcacheManager”)的方法改為如下:

    @Bean(name = "ehcacheManager")
    public CacheManager ehCacheManagerFactoryBean() {
          CacheManager cacheManager = CacheManager.getCacheManager("es");
            if(cacheManager == null){
                try {
                    cacheManager = CacheManager.create(ResourceUtils.getInputStreamForPath("classpath:ehcache.xml"));
                } catch (IOException e) {
                    throw new RuntimeException("initialize cacheManager failed");
                }
            }
           return cacheManager;
    }

es是我建立的ehcache.xml中定義的ehcache名稱!!

至此問題解決!!!!

結語

Spring boot的熱部署很好用,以前一般手動開應用要20多秒,現在修改好這個問題之後,自動重啟應用只需要幾秒!!!雖然我一直知道是重啟應用時重複導致的,但只想到在定義bean的時候先銷燬(啟動報錯),沒有想到這樣做判斷!總的來說還是不知道CacheManager cacheManager = CacheManager.getCacheManager("es");這樣初始化!