tomcat的热部署实现原理:tomcat启动的时候会有启动一个线程每隔一段时间会去判断应用中加载的类是否发生变法(类总数的变化,类的修改),如果发生了变化就会把应用的启动的线程停止掉,清除引用,并且把加载该应用的WebappClassLoader设为null,然后创建一个新的WebappClassLoader来重新加载应用。 tomcat中热部署发现类变法之后要做的一系列停止工作的时序图如下:
上面时序图中只把关键的流转步骤画出来了,还有一些细节的处理没有完全画出来,这部分代码的继承的结构还是比较复杂的,debug上面的流程的时候,各种跳转。先解释一下上面的时序图,这个时序图是从WebappLoader的backgroundProcess()方法开始的,backgroundProcess()就是随tomcat启动的时候启动的线程中其中的一方法,是tomcat热部署代码的入口。 其中最关键的两步。就是WebappLoader的stopInternal()方法和WebappClassLoader的stop()方法。 在WebappLoader的stopInternal()中
protected void stopInternal() throws LifecycleException { ….. // Throw away our current class loader ((Lifecycle) classLoader).stop(); …….. classLoader = null; }
在 ((Lifecycle) classLoader).stop(); 中classLoader是WebappClassLoader的实例。也就是调用了WebappClassLoader的stop()方法。如下:
public void stop() throws LifecycleException { // Clearing references should be done before setting started to // false, due to possible side effects clearReferences(); started = false; int length = files.length; for (int i = 0; i < length; i++) { files[i] = null; } length = jarFiles.length; for (int i = 0; i < length; i++) { try { if (jarFiles[i] != null) { jarFiles[i].close(); } } catch (IOException e) { // Ignore } jarFiles[i] = null; } notFoundResources.clear(); resourceEntries.clear(); resources = null; repositories = null; repositoryURLs = null; files = null; jarFiles = null; jarRealFiles = null; jarPath = null; jarNames = null; lastModifiedDates = null; paths = null; hasExternalRepositories = false; parent = null; permissionList.clear(); loaderPC.clear(); if (loaderDir != null) { deleteDir(loaderDir); } }
可以看到stop()方法做了很多清除引用的工作。其中 clearReferences() 中
protected void clearReferences() { // De-register any remaining JDBC drivers clearReferencesJdbc(); // Stop any threads the web application started clearReferencesThreads(); // Check for leaks triggered by ThreadLocals loaded by this class loader checkThreadLocalsForLeaks(); // Clear RMI Targets loaded by this class loader clearReferencesRmiTargets(); // Null out any static or final fields from loaded classes, // as a workaround for apparent garbage collection bugs if (clearReferencesStatic) { clearReferencesStaticFinal(); } // Clear the IntrospectionUtils cache. IntrospectionUtils.clear(); // Clear the classloader reference in common-logging if (clearReferencesLogFactoryRelease) { org.apache.juli.logging.LogFactory.release(this); } // Clear the resource bundle cache // This shouldn’t be necessary, the cache uses weak references but // it has caused leaks. Oddly, using the leak detection code in // standard host allows the class loader to be GC’d. This has been seen // on Sun but not IBM JREs. Maybe a bug in Sun’s GC impl? clearReferencesResourceBundles(); // Clear the classloader reference in the VM’s bean introspector java.beans.Introspector.flushCaches(); }
还进行了清除应用线程等工作。最后在WebappClassLoader的stopInternal()方法中执行了 classLoader = null; 那这个类加载器的实例就没有被引用了。 最后调用WebappLoader中的startInternal()方法,创建新的WebappClassLoader实例,然后开始重新加载应用。到此tomcat的热部署流程就完成了。