[Tomcat源码系列]结构解析 2)生命期控制结构
一、生命期控制结构基础
Tomcat的生命期控制是一个两层的结构
1)Lifecycle(org.apache.catalina.Lifecycle):
在前一篇 中看到的各组件(Service、Connector、Engine、Host、Context、Wrapper)都会实现这个接口,我们看看这个接口的定义
public interface Lifecycle { public static final String INIT_EVENT = "init"; public static final String START_EVENT = "start"; public static final String BEFORE_START_EVENT = "before_start"; public static final String AFTER_START_EVENT = "after_start"; public static final String STOP_EVENT = "stop"; public static final String BEFORE_STOP_EVENT = "before_stop"; public static final String AFTER_STOP_EVENT = "after_stop"; public static final String DESTROY_EVENT = "destroy"; public static final String PERIODIC_EVENT = "periodic"; public void addLifecycleListener(LifecycleListener listener); public LifecycleListener[] findLifecycleListeners(); public void removeLifecycleListener(LifecycleListener listener); public void start() throws LifecycleException; public void stop() throws LifecycleException; }
2)LifecycleListener (org.apache.catalina.LifecycleListener):
在我们上一篇看到HostConfig就是输入LifecycleListener。
如下是LifecycleLister的定义
public interface LifecycleListener { public void lifecycleEvent(LifecycleEvent event); }
二、Tomcat初始化处理过程1.下面是上一个范例中涉及到的处理器初始化主要过程,可以看出来,通过类似的结构,实现了层层的初始化过程
--StandardEngine.init
--super(ContainerBase).start
--fireLifecycleEventBEFORE_START_EVENT
--EngineConfig.lifecycleEvent(基本上啥事不干,可忽略)
--initChildren
--StandardHost.start(见2)
--fireLifecycleEventSTART_EVENT
--fireLifecycleEventAFTER_START_EVENT
2.StandardHost.start
--StandardHost.init
--super(ContainerBase).start
--fireLifecycleEventBEFORE_START_EVENT
--HostConfig.lifecycleEvent(识别出Context,并向StandardHost注册)
--initChildren
--StandardContext.start(见3)
--fireLifecycleEventSTART_EVENT
--fireLifecycleEventAFTER_START_EVENT
3.StandardContext.start
--StandardContext.init
--super(ContainerBase).start
--fireLifecycleEventBEFORE_START_EVENT
--ContextConfig.lifecycleEvent(解析web.xml,并向StandardContxt注册Wrapper)
--initChildren
--StandardWrapper.start
--fireLifecycleEventSTART_EVENT
--fireLifecycleEvent AFTER_START_EVENT2.我们从StandardEngine为入口,看看几个重要的初始化过程的代码1)Standard.start
public void start() throws LifecycleException { if( started ) { return; } if( !initialized ) { init(); //初始化自己 } // Look for a realm - that may have been configured earlier. // If the realm is added after context - it'll set itself. if( realm == null ) { ObjectName realmName=null; try { realmName=new ObjectName( domain + ":type=Realm"); if( mserver.isRegistered(realmName ) ) { mserver.invoke(realmName, "init", new Object[] {}, new String[] {} ); } } catch( Throwable t ) { log.debug("No realm for this engine " + realmName); } } // Log our server identification information //System.out.println(ServerInfo.getServerInfo()); if(log.isInfoEnabled()) log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo()); if( mbeans != null ) { try { Registry.getRegistry(null, null) .invoke(mbeans, "start", false); } catch (Exception e) { log.error("Error in start() for " + mbeansFile, e); } } //ContainerBase.start super.start(); }
非常简单明了,初始化自己、注册JMX、super.start三步骤,我们进入下一步2)ContainerBase.start
public synchronized void start() throws LifecycleException { // Validate and update our current component state if (started) { if(log.isInfoEnabled()) log.info(sm.getString("containerBase.alreadyStarted", logName())); return; } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); started = true; // Start our subordinate components, if any if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); logger = null; getLogger(); if ((logger != null) && (logger instanceof Lifecycle)) ((Lifecycle) logger).start(); if ((manager != null) && (manager instanceof Lifecycle)) ((Lifecycle) manager).start(); if ((cluster != null) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); if ((realm != null) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Start our child containers, if any Container children[] = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } // Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(START_EVENT, null); // Start our thread threadStart(); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
如我们上面所说,也是几个简单的过程:触发初始化事件、初始化各组件、初始化children(层次间的关系通过children实现)。注意,ContainerBase本身会继承Lifecycle接口和Container接口,而StandardEngine、StandardHost、StandardContext、StandardWrapper都继承自ContainerBase,因此初始化方式非常类似。3)HostConfig,HostConfig是一个LifecycleListener,如我们上一篇的DEMO代码,作为LifecycleListener注册到StandardHost中,在ContainerBase初始化开始阶段,会触发一个BEFORE_START_EVENT,触发HostConfig的初始化,HostConfig初始化过程是:HostConfig. lifecycleEvent-->HostConfig.start-->HostConfig. deployApps,我们看看HostConfig. deployApps是如何实现的
protected void deployApps() { File appBase = appBase(); File configBase = configBase(); // Deploy XML descriptors from configBase deployDescriptors(configBase, configBase.list()); // Deploy WARs, and loop if additional descriptors are found deployWARs(appBase, appBase.list()); // Deploy expanded folders deployDirectories(appBase, appBase.list()); }
可以看到,是3种类型,分别是{tomcat}/conf/{hostname}/目录下的{context.xml}、{tomcat}/{appBase}/下的war和{tomcat}/{appBase}下的目录。我们再看看HostConfig. deployDirectory是如何实现的
protected void deployDirectory(String contextPath, File dir, String file) { DeployedApplication deployedApp = new DeployedApplication(contextPath); if (deploymentExists(contextPath)) return; // Deploy the application in this directory if( log.isDebugEnabled() ) log.debug(sm.getString("hostConfig.deployDir", file)); try { Context context = (Context) Class.forName(contextClass).newInstance(); if (context instanceof Lifecycle) { Class clazz = Class.forName(host.getConfigClass()); LifecycleListener listener = (LifecycleListener) clazz.newInstance(); ((Lifecycle) context).addLifecycleListener(listener); } context.setPath(contextPath); context.setDocBase(file); File configFile = new File(dir, Constants.ApplicationContextXml); if (deployXML) { context.setConfigFile(configFile.getAbsolutePath()); } host.addChild(context); deployedApp.redeployResources.put(dir.getAbsolutePath(), new Long(dir.lastModified())); if (deployXML) { deployedApp.redeployResources.put(configFile.getAbsolutePath(), new Long(configFile.lastModified())); } addWatchedResources(deployedApp, dir.getAbsolutePath(), context); } catch (Throwable t) { log.error(sm.getString("hostConfig.deployDir.error", file), t); } deployed.put(contextPath, deployedApp); }
实际上也没有太多复杂的东西,需要更细节的了解可以看org.apache.catalina.startup.HostConfig的代码4)ContextConfig,与HostConfig类似,我们这里直接进入ContextConfig.start
protected synchronized void start() { ...略 // Set properties based on DefaultContext Container container = context.getParent(); ...略 // Process the default and application web.xml files defaultWebConfig(); applicationWebConfig(); if (!context.getIgnoreAnnotations()) { applicationAnnotationsConfig(); } if (ok) { validateSecurityRoles(); } // Configure an authenticator if we need one if (ok) authenticatorConfig(); ...略 }
其中defaultWebConfig解析{tomcat}/config/web.xml(提供JSP和welcome file的支持),而applicationWebConfig当然是每个应用的WEB-INF/web.xml了。解析的全过程不再细节描述,可以参见org.apache.catalina.startup.ContextConfig
5)在初始化过程当中,向Container(Engine、Host、Context、Config)注册child(addChild方法)的过程是需要重点关注的地方,由于跟后续请求处理的过程相关联,因此放在后面再做解析
三、通过如上层层初始化,我们的StandardEngine创建完毕,一切准备就绪,可以等待请求接入