Tomcat7启动分析(四)各组件init、start方法调用

在正常启动Tomcat7的情况下,上篇文章分析到了执行org.apache.catalina.core.StandardServer的init和start方法这儿,那么就来看看这两个方法里面到底干了些什么。

但是在StandardServer类里面并没有发现这两个方法:


由此推知这两方法必定是在该类的父类中已实现了,在StandardServer类的父类LifecycleMBeanBase类的父类LifecycleBase类里面终于找到了这两个方法的实现,下面先来看下init方法:

    @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
        setStateInternal(LifecycleState.INITIALIZING, null, false);

        try {
            initInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }

        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
    
    
    protected abstract void initInternal() throws LifecycleException;

先将干扰程序阅读视线的setStateInternal方法调用忽略掉(下一篇文章会详细讲解该方法),发现这里面就做了一件事情,调用了一下接下来定义的抽象方法initInternal()(第21行)。实际上看下LifecycleBase的实现类就会发现,所有的组件类几乎都继承了LifecycleBase类,所以这些组件类一般只会有initInternal方法的定义。(这里所说的组件类就是前面我们分析Digester解析时发现的StandardServer、StandardService、StandardEngine、StandardHost、StandardContext等类)

这里所说的组件可以将其理解为我们最开始分析server.xml时xml文件里的各个节点,父子关系也即xml文件里的父子节点。浏览下LifecycleBase的子类就会发现节点的实现类都是这个类的子类(记住这点,后面会提到)。


 

同样分析start方法:

    /**
     * {@inheritDoc}
     */
    @Override
    public final synchronized void start() throws LifecycleException {
        
        if (LifecycleState.STARTING_PREP.equals(state) ||
                LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {
            
            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted",
                        toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted",
                        toString()));
            }
            
            return;
        }
        
        if (state.equals(LifecycleState.NEW)) {
            init();
        } else if (state.equals(LifecycleState.FAILED)){
            stop();
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        setStateInternal(LifecycleState.STARTING_PREP, null, false);

        try {
            startInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.startFail",toString()), t);
        }

        if (state.equals(LifecycleState.FAILED) ||
                state.equals(LifecycleState.MUST_STOP)) {
            stop();
        } else {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            if (!state.equals(LifecycleState.STARTING)) {
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            }
            
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    }


    /**
     * Sub-classes must ensure that the state is changed to
     * {@link LifecycleState#STARTING} during the execution of this method.
     * Changing state will trigger the {@link Lifecycle#START_EVENT} event.
     * 
     * If a component fails to start it may either throw a
     * {@link LifecycleException} which will cause it's parent to fail to start
     * or it can place itself in the error state in which case {@link #stop()}
     * will be called on the failed component but the parent component will
     * continue to start normally.
     * 
     * @throws LifecycleException
     */
    protected abstract void startInternal() throws LifecycleException;

第7到21行是start功能的前置校验,这里如果发现start方法已经调用过了,将会记录日志并直接返回。第23到30行如果发现start放的需要做的前置方法没有调用完,或者调用出错,将会先调用这些前置方法。第32行暂时先不管,不影响程序阅读,第35行是该方法的实质,将会调用本类中定义的抽象方法startInternal()(第71行)。下面的代码同上述一样,都是一些start方法调用过程中可能出现的错误的错误处理。

从以上init和start方法的定义可以看到这两个方法最终将会调用子类中定义的initInternal和startInternal。

接回本文开头,一开始在找StandardServer类中init和start方法的定义,看完了上面的分析发现最终会调用StandardServer类的initInternal和startInternal两个方法。那这两个方法里面干了些什么?

initInternal方法:

    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {
        
        super.initInternal();

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");

        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");
        
        // Register the naming resources
        globalNamingResources.init();
        
        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

init方法里面做了好几件事情,牵涉的话题比较多,这里重点关注最后第53到55行的代码,这里将循环调用Server类里内置的Service数组的init方法。

startInternal方法:

    /**
     * Start nested components ({@link Service}s) and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();
        
        // Start our defined Services
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

重点关注第17到21行,同上一段所分析的代码类似,将循环调用Sever类里内置的Service数组的start方法。

那么这里提到的Service的对象到底是什么?

上篇文章分析Digester时提到“经过对xml文件的解析将会产生org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。”

所以正常情况下这里的Service将会是org.apache.catalina.core.StandardService的对象(该代码见org.apache.catalina.startup.Catalina类的339到341行)。

所以按上面的分析,接下来将会调用StandardService类的init和start方法,实际上这个类也是LifecycleBase类的子类,所以最终的也会调用本类中的initInternal和startInternal方法。

initInternal方法:

    /**
     * Invoke a pre-startup initialization. This is used to allow connectors
     * to bind to restricted ports under Unix operating environments.
     */
    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();
        
        if (container != null) {
            container.init();
        }

        // Initialize any Executors
        for (Executor executor : findExecutors()) {
            if (executor instanceof LifecycleMBeanBase) {
                ((LifecycleMBeanBase) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize our defined Connectors
        synchronized (connectors) {
            for (Connector connector : connectors) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed", connector);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

这里将会调用Service下的各类子组件中的init方法。

startInternal方法:

    /**
     * Start nested components ({@link Executor}s, {@link Connector}s and
     * {@link Container}s) and implement the requirements of
     * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected void startInternal() throws LifecycleException {

        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        if (container != null) {
            synchronized (container) {
                container.start();
            }
        }

        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }

        // Start our defined Connectors second
        synchronized (connectors) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }

同理,将会调用service下各类子组件中的start方法。

StandardService的子容器是StandardEngine,看下StandardEngine的startInternal方法:

    protected synchronized void startInternal() throws LifecycleException {
        
        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());

        // Standard container startup
        super.startInternal();
    }

这里不同于上面两级容器的实现,而是直接调用了父类的startInternal方法:

    protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        if ((loader != null) && (loader instanceof Lifecycle))
            ((Lifecycle) loader).start();
        logger = null;
        getLogger();
        if ((manager != null) && (manager instanceof Lifecycle))
            ((Lifecycle) manager).start();
        if ((cluster != null) && (cluster instanceof Lifecycle))
            ((Lifecycle) cluster).start();
        Realm realm = getRealmInternal();
        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();
        List<Future<Void>> results = new ArrayList<Future<Void>>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }
        if (fail) {
            throw new LifecycleException(
                    sm.getString("containerBase.threadedStartFailed"));
        }

        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();


        setState(LifecycleState.STARTING);

        // Start our thread
        threadStart();

    }

第19到34行即启动当前容器下的子容器的代码,这里采用了分线程分别启动的方式。核心的调用子容器的start方法的代码在StartChild类的call方法中:

    private static class StartChild implements Callable<Void> {

        private Container child;

        public StartChild(Container child) {
            this.child = child;
        }

        @Override
        public Void call() throws LifecycleException {
            child.start();
            return null;
        }
    }

这里使用了JDK5的新的执行线程的方式,不理解的话请参考相关文档说明。

StandardHost中的startInternal与StandardEngine类似,这里不再赘述,建议有兴趣的朋友逐一分析Review一遍,碰到组件里面嵌套的变量不知道具体实现类的就从上篇文章里面提到的createStartDigester那边开始找起,这里不能直接找到的就在里面提到的new *RuleSet的addRuleInstances方法里面找。通过这种调用将会最终执行完所有在server.xml里配置的节点的实现类中initInternal和startInternal方法。


最后上面提到的org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等组件的这两个方法都会调用到。

就这样,Tomcat7在内存中为这一连串组件产生对象,建立对象调用关系,调用它们各自的初始化和启动方法,启动的总体过程就介绍完了,这些对象start之后将会响应客户端的请求,为用户服务了。当然,这里还没有涉及到对于具体的发布到tomcat里面的没有应用的载入过程,web应用中配置的servlet、filter、listener等的载入、启动服务过程,浏览器发起的一个请求如何经过Tomcat内各组件的流转调用到具体应用里去的,这一系列问题都还没谈到。因为Tomcat本身庞大繁杂,需要找一个视角切入进去,为了叙述的简单,先从整体上对Tomcat内包含的各组件产生机制有一个大体轮廓的了解,这样为后面的介绍提供一个统一的背景。

下一篇文章将分析本文开头遗留的一个问题——setStateInternal方法的作用,以及Tomcat中的Lifecycle实现原理。

猜你喜欢

转载自tyrion.iteye.com/blog/1920564