Context#backgroundProcess
StandardContext实现类中:
@Override public void backgroundProcess() { if (!getState().isAvailable()) return; // WebappLoader周期性检查 // WEB-INF/classes、WEB-INF/lib 目录下的类文件是否有更新 Loader loader = getLoader(); if (loader != null) { loader.backgroundProcess(); } // Session管理器周期性检查是否有Session过期 Manager manager = getManager(); if (manager != null) { manager.backgroundProcess(); } // 周期性检查静态资源是否有更新 WebResourceRoot resources = getResources(); if (resources != null) { resources.backgroundProcess(); } super.backgroundProcess(); }
WebappLoader是如何实现热加载的呢?
关键是调用Context#reload方法:
停止和销毁Context容器及其所有子容器(Wrapper),即Wrapper里的Servlet实例也被销毁
停止和销毁Context容器关联的Listener和Filter
停止和销毁Context下的Pipeline和各种Valve
停止和销毁Context的类加载器,以及类加载器加载的类文件资源
启动Context容器,在这个过程中会重新创建前面四步被销毁的资源。
在这个过程中,类加载器发挥着关键作用。一个Context容器对应一个类加载器,类加载器在销毁的过程中会把它加载的所有类也全部销毁。Context容器在启动过程中,会创建一个新的类加载器来加载新的类文件。
在Context的reload方法里,并没有调用Session管理器的destroy方法,也就是说这个Context关联的Session是没有销毁的。
Tomcat热加载默认是关闭的,需在conf目录下的context.xml文件中设置reloadable参数开启:
<Context reloadable="true"/>
Tomcat热部署
跟热加载的本质区别是:
热部署会重新部署Web应用,原Context对象会被整个被销毁,因此该Context所关联一切资源都会被销毁,包括Session。
Tomcat热部署由哪个容器实现呢?
不是由Context,因为热部署过程中Context容器被销毁了,所以就是Host,Context的父容器。
Host容器并未在backgroundProcess中实现周期性检测,而是通过监听器HostConfig实现。
HostConfig就是前面提到的“周期事件”的监听器,“周期事件”达到时,HostConfig会做什么呢?
public void lifecycleEvent(LifecycleEvent event) { // 执行check if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) { check(); } }
protected void check() { if (host.getAutoDeploy()) { // 检查这个Host下所有已经部署的Web应用 DeployedApplication[] apps = deployed.values().toArray(new DeployedApplication[0]); for (int i = 0; i < apps.length; i++) { //检查Web应用目录是否有变化 checkResources(apps[i], false); } // 执行部署 deployApps(); } }
HostConfig会检查webapps目录下的所有Web应用:
- 如果原来Web应用目录被删掉了,就把相应Context容器整个销毁掉
- 是否有新的Web应用目录放进来了,或者有新的WAR包放进来了,就部署相应的Web应用
因此HostConfig做的事情都是比较“宏观”的,它不会去检查具体类文件或者资源文件是否有变化,而是检查Web应用目录级别变化。