[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.下面是上一个范例中涉及到的处理器初始化主要过程,可以看出来,通过类似的结构,实现了层层的初始化过程
1.StandardEngine.start
--StandardEngine.init
--super(ContainerBase).start
--fireLifecycleEvent BEFORE_START_EVENT
--EngineConfig. lifecycleEvent(基本上啥事不干,可忽略)
--initChildren
--StandardHost.start(见2)
--fireLifecycleEvent START_EVENT
--fireLifecycleEvent AFTER_START_EVENT
2.StandardHost.start
--StandardHost.init
--super(ContainerBase).start
--fireLifecycleEvent BEFORE_START_EVENT
--HostConfig. lifecycleEvent(识别出Context,并向StandardHost注册)
--initChildren
--StandardContext.start(见3)
--fireLifecycleEvent START_EVENT
--fireLifecycleEvent AFTER_START_EVENT
3.StandardContext.start
--StandardContext.init
--super(ContainerBase).start
--fireLifecycleEvent BEFORE_START_EVENT
--ContextConfig. lifecycleEvent(解析web.xml,并向StandardContxt注册Wrapper)
--initChildren
--StandardWrapper.start
--fireLifecycleEvent START_EVENT
--fireLifecycleEvent AFTER_START_EVENT
2.我们从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 instanceof Lifecycle)
((Lifecycle) children).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创建完毕,一切准备就绪,可以等待请求接入
页:
[1]