一、問題
Idea開發工具想使用devtools實現開發過程中的熱部署,添加依賴如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.1.8.RELEASE</version>
<optional>true</optional>
</dependency>
項目中還使用了ehcache緩存和redis緩存,單機情況下使用ehcache緩存,添加devtools依賴后,更改html模板的內容后,項目會重新重啟,但是在獲取緩存中內容的時候報異常。
跟蹤代碼到如下位置:
錯誤信息為:
The CacheManager has been shut down. It can no longer be used.
查看getEhcache方法:
二、解決方法
經分析為cacheManager在熱啟動的時候被關閉關閉,但是對象沒有被重新創建還是用的原來
所以需要重新實例化一個cacheManager,原來的代碼如下:
現在改為:
private net.sf.ehcache.CacheManager getCacheManager() { net.sf.ehcache.CacheManager cacheManager = ((EhCacheCacheManager) this.ehCacheCacheManager).getCacheManager(); if (cacheManager == null || cacheManager.getStatus().intValue() == 2) {//等於2表示當前狀態為關閉狀態 //在spring boot devtools 頁面重啟的時候,cacheManager會報當前緩存已經關閉的異常, // 所以在狀態為關閉情況下,這里需要重新設置一下 try { CacheManager cacheManager1 = CacheManager.create(ResourceUtils.getInputStreamForPath("classpath:ehcache.xml")); ((EhCacheCacheManager) this.ehCacheCacheManager).setCacheManager(cacheManager1); return cacheManager1; } catch (Exception e) { throw new RuntimeException("initialize cacheManager failed"); } } return cacheManager; }
問題解決了
但是如果采用的是memorySessionDao存儲的session,重啟之后session會丟失,這樣需要對開發環境進行額外的攔截規則判斷,如果不想修改shiro的攔截器或增加白名單的方式,可以采用ehcacheSessionDao的方式存儲session信息,修改shiroconfig的配置:
public SessionDAO sessionDAO() { /* SessionDAO sessionDAO = null; {sessionDAO = new MemorySessionDAO()*/ EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO(); sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache"); return sessionDAO; }
defaultWebSessionManager的配置不用修改:
public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager, Collection<SessionListener> listeners, SessionDAO sessionDAO) { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager (); …… sessionManager.setSessionDAO(sessionDAO); return sessionManager; }
配置ehcache.xml文件,增加shiro-activeSessionCache,設置overflowToDisk=true,存儲到硬盤中
這樣,重啟之后就不用重新登錄系統了
還有其他問題:
在熱部署重新啟動后,發現寫日志的靜態方法報數據庫連接已關閉的異常,但是除了這個類之外其它訪問數據庫的操作又都正常,原來寫日志的類采用的都是靜態方法,而對應的數據庫日志表的mapper是在該類的靜態變量中聲明的
猜測是熱部署重啟之后,datasource先是shutdown,而后新創建datasource對象,而日志類中靜態變量不是采用authowire自動注入的方式,依然保持原來的mapper對象,所以訪問數據庫的時候就提示數據庫連接已經關閉。
修改方式,將該私有全局靜態變量放到方法中賦值:
private static void insertSecurityLog(SysSecurityLog sysSecurityLog) { SysSecurityLogMapper securityLogMapper = SpringCtxHolder.getBean(SysSecurityLogMapper.class); securityLogMapper.insert(sysSecurityLog); }
參考地址:
解決springboot集成dubbo並使用外部容器部署在應用重啟出現dataSource has already close 異常
還有一個通過監控事件的方式的文章:
SpringBoot Test 多線程報錯:dataSource already closed