(1) 一种是类似于Servlet这种类的热部署即在WEB-INF/class目录下的类:实现方式可以概括为在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳变掉了,则调用容器的reload的方法,将类重新载入。
那么具体的分析一下:
tomcat用来加载servlet的类加载器是WebappClassLoader,但热部署的逻辑不是在这个类加载器里,而是封装在了外围的WebappLoader里面(WebappClassLoader 是WebappLoader的成员变量)。WebappLoader作为一个加载器,其实现了Loader接口,loader接口中定义了两个和热部署密切相关的方法即modified方法和backgroundProcess方法。Modified方法关联了载入器中资源的变化情况,而backgroundProcess则定义了后台线程定时扫描时具体要执行的逻辑。
Tomcat的启动具体可以分解为各级容器的启动(即:Engine,host,context 注意不包含Wrapper,Wrapper是在具体请求的时候初始化的)和连接器(默认两种连接器Http,ajp)的启动,另外当然还做了一些配置文件的解析这个不多说。容器启动根据tomcat的生命周期启动方式是调用容器的start方法,start方法调用了具体的startInternal方法来做一些初始化的工作,其中就包括检测类文件变化的后台线程的初始化及启动,具体看一下ContainerBase的StartInternal方法:
在这个方法的最后一行,调用了threadStart()方法,这个方法就是在启动后台线程,看一下方法体:
由这段代码可知,这个方法启动了一条线程,其中ContainerBackgroundProcessor一定是实现了Runnable接口的具体的后台线程执行的逻辑,那就继续看:
至此就看到了后台线程的定义的真实面目,其中有个变量backgroundProcessDelay 这个变量的值会影响这条线程的睡眠时间,那也就说后台线程定时执行的时间是可配置的。最后一行调用了processChildren方法,这个方法又是具体的执行逻辑,那么断点调试看下是如何完成了类的重载的。
ProcessChildren方法定义如下:
这里调用了容器的backgroundProcess方法。Debug信息如下:
这个方法调用了载入器的WebappLoader的backgroundProcess方法。
WebappLoader的backgroundProcess方法如下:
标注处有两个校验项,一个是reloadable一个是modified方法。这个reloadable就是是否执行热部署的开关,这个可以在$CATALINA_HOME/conf/context.xml 当中配置一下,这个开关默认是不打开的。我修改了一下配置项如下:
Context.xml:
添加reloadable=true后,即可完成context容器的类的热部署。修改之后调试信息如下:
再来看一下第二个校验方法modified方法。这个modified方法会调用WebappClassLoader的Modified来判断文件是否被修改。如果两个校验项都为真的话,就会调用容器的reload方法(这里说的容器是Context容器)
Context容器的这个reload方法定义如下:
这个reload方法实现的还是很简单粗暴的,就是讲整个Context容器重启了一下,先stop,然后再start.(容器都重启了,那容器课件范围的类自然会被重新加载了)
以上就是tomcat7.x实现WEB-INF/classes当中类热部署的大体流程。除了WEB-INF/classes当中的类之外,还有一种特殊的类是默认热部署的,这个就是JSP。
(2)为了实现JSP的加载部署,tomcat实现了另一个加载器即org.apache.jasper.servlet.JasperLoader.JSP属于一次消费品,每次访问都会重新加载(网上说的,这个源代码下次再研究下,在这先记录下)
(3)在读源码的时候身边研究了下$CATALINA_HOME/conf/web.xml,这个配置文件是tomcat内置的配置文件,主要配置了两个Servlet. 具体的配置如下:
DefaultServlet是要作用是为静态资源请求提供服务,包括图片、html等。还有一个用来解析JSP的servlet: