一、问题
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