对重载的支持
tomcat里容器对重载功能的支持是依靠Load的(在目前就是WebLoader)。当在绑定载入器的容器时
public void setContainer(Container container) { ... // Register with the new Container (if any) if ((this.container != null) && (this.container instanceof Context)) { setReloadable( ((Context) this.container).getReloadable() ); ((Context) this.container).addPropertyChangeListener(this); } }
可见载入器的reloadable与它所绑定的容器的reloadable是一致的。
我们再看看载入器的setReloadable
public void setReloadable(boolean reloadable) { // Process this property change boolean oldReloadable = this.reloadable; this.reloadable = reloadable; .... if (!started) return; if (!oldReloadable && this.reloadable) // threadStart(); else if (oldReloadable && !this.reloadable) threadStop(); }
之前在第八章的时候,我们就知道congext容器reloadable的默认值是false,而且载入器的reloadable也是false。
因而默认情况下,载入器的run方法不会运作。那载入器的start没有启动run方法么?
public void start() throws LifecycleException { ..... // Start our background thread if we are reloadable if (reloadable) { log(sm.getString("webappLoader.reloading")); try { threadStart(); } catch (IllegalStateException e) { throw new LifecycleException(e); } } }
而载入器的run方法干什么?就是用
while (!threadDone) { // Wait for our check interval threadSleep(); if (!started) break; try { // Perform our modification check if (!classLoader.modified()) //WebappClassLoader的modified是根据时间判定所监 continue; //控的class是否改变 } catch (Exception e) { //一旦改变了 返回true 就 notifyContext log(sm.getString("webappLoader.failModifiedCheck"), e); continue; } // Handle a need for reloading notifyContext(); break; }
notifyContext()会启用WebappContextNotifier,后者调用容器的reload方法。
不过上面说了这么多,前提条件是载入器的reloadable得是true....
backgroundProcess 方法
在tomcat5中(上面说的那些是基于tomcat4的),关于时间戳的检查交给了backgroundProcess。
现在有个问题,4里好好的,为什么到5里就改了呢?
改是有原因的嘛。
想想之前,session因为要检查过期时间得启用一个线程,关于重载得检查时间戳,还得启用一个线程...
因而,在tomcat5中,所以的后台处理程序共用一个线程。具体怎么办呢?听老夫给你细细道来。
后面的所有代码来自tomcat7.0.55
在tomcat中,ContainerBase里面有
protected void threadStart() { if (thread != null) return; if (backgroundProcessorDelay <= 0) return; threadDone = false; String threadName = "ContainerBackgroundProcessor[" + toString() + "]"; thread = new Thread(new ContainerBackgroundProcessor(), threadName); thread.setDaemon(true); thread.start(); }
重启了一个线程,不用说关键问题就是ContainerBackgroundProcessor。而它是ContainerBase的一个内部类。
ContainerBackgroundProcessor在run方法里会周期性的调用processChildren方法。 protected void processChildren(Container container, ClassLoader cl) { ... container.backgroundProcess(); ... Container[] children = container.findChildren(); for (int i = 0; i < children.length; i++) { if (children[i].getBackgroundProcessorDelay() <= 0) { processChildren(children[i], cl); } } }
processChildren会先调用自己的backgroundProcess,然后让自己的子孙们也走一遍自己的流程。
public void backgroundProcess() { if (!getState().isAvailable()) return; if (loader != null) { try { loader.backgroundProcess(); //载入器的后台程序 } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e); } } if (manager != null) { try { manager.backgroundProcess(); //session的后台程序 } catch (Exception e) { log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e); } } }
那具体的载入器的backgroundProcess怎么写呢?如下
WebappLoader.java public void backgroundProcess() { if (reloadable && modified()) { try { Thread.currentThread().setContextClassLoader (WebappLoader.class.getClassLoader()); if (container instanceof StandardContext) { ((StandardContext) container).reload(); } } finally { if (container.getLoader() != null) { Thread.currentThread().setContextClassLoader (container.getLoader().getClassLoader()); } } } else { closeJARs(false); } }