[Tomcat源码系列]结构解析 2)生命期控制结构

一、生命期控制结构基础

Tomcat的生命期控制是一个两层的结构

1)Lifecycle(org.apache.catalina.Lifecycle):

    在前一篇 中看到的各组件(Service、Connector、Engine、Host、Context、Wrapper)都会实现这个接口,我们看看这个接口的定义
Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

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的定义
Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}

二、Tomcat初始化处理过程1.下面是上一个范例中涉及到的处理器初始化主要过程,可以看出来,通过类似的结构,实现了层层的初始化过程

1.StandardEngine.start

--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_EVENT

2.我们从StandardEngine为入口,看看几个重要的初始化过程的代码1)Standard.start

Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

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

Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

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是如何实现的

Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

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是如何实现的

Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

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

Java代码 [Tomcat源码系列]结构解析 2)生命期控制结构

 

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创建完毕,一切准备就绪,可以等待请求接入

相关推荐