首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

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

2012-10-07 
[Tomcat源码系列]结构解析 2)生命期控制结构一、生命期控制结构基础Tomcat的生命期控制是一个两层的结构1)L

[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.下面是上一个范例中涉及到的处理器初始化主要过程,可以看出来,通过类似的结构,实现了层层的初始化过程

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

热点排行