Tomcat 7 啓動分析(四)各組件 init、start 方法調用

在正常啓動 Tomcat 7 的狀況下,上篇文章分析到了執行 org.apache.catalina.core.StandardServer 的 init 和 start 方法這兒,那麼就來看看這兩個方法裏面到底幹了些什麼。web

可是在 StandardServer 類裏面並無發現這兩個方法: apache

由此推知這兩方法一定是在該類的父類中已實現了,在 StandardServer 類的父類 LifecycleMBeanBase 類的父類 LifecycleBase 類裏面終於找到了這兩個方法的實現,下面先來看下 init 方法:

1	    @Override
     2	    public final synchronized void init() throws LifecycleException {
     3	        if (!state.equals(LifecycleState.NEW)) {
     4	            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
     5	        }
     6	        setStateInternal(LifecycleState.INITIALIZING, null, false);
     7	
     8	        try {
     9	            initInternal();
    10	        } catch (Throwable t) {
    11	            ExceptionUtils.handleThrowable(t);
    12	            setStateInternal(LifecycleState.FAILED, null, false);
    13	            throw new LifecycleException(
    14	                    sm.getString("lifecycleBase.initFail",toString()), t);
    15	        }
    16	
    17	        setStateInternal(LifecycleState.INITIALIZED, null, false);
    18	    }
    19	    
    20	    
    21	    protected abstract void initInternal() throws LifecycleException;
複製代碼

先將干擾程序閱讀視線的 setStateInternal 方法調用忽略掉(下一篇文章會詳細講解該方法),發現這裏面就作了一件事情,調用了一下接下來定義的抽象方法 initInternal()(第 21 行)。實際上看下 LifecycleBase 的實現類就會發現,全部的組件類幾乎都繼承了 LifecycleBase 類,因此這些組件類通常只會有 initInternal 方法的定義。(這裏所說的組件類就是前面咱們分析 Digester 解析時發現的 StandardServer、StandardService、StandardEngine、StandardHost、StandardContext 等類)數組

這裏所說的組件能夠將其理解爲咱們最開始分析 server.xml 時 xml 文件裏的各個節點,父子關係也即 xml 文件裏的父子節點。瀏覽下 LifecycleBase 的子類就會發現節點的實現類都是這個類的子類(記住這點,後面會提到)。 瀏覽器

一樣分析 start 方法:

1	    /**
     2	     * {@inheritDoc}
     3	     */
     4	    @Override
     5	    public final synchronized void start() throws LifecycleException {
     6	        
     7	        if (LifecycleState.STARTING_PREP.equals(state) ||
     8	                LifecycleState.STARTING.equals(state) ||
     9	                LifecycleState.STARTED.equals(state)) {
    10	            
    11	            if (log.isDebugEnabled()) {
    12	                Exception e = new LifecycleException();
    13	                log.debug(sm.getString("lifecycleBase.alreadyStarted",
    14	                        toString()), e);
    15	            } else if (log.isInfoEnabled()) {
    16	                log.info(sm.getString("lifecycleBase.alreadyStarted",
    17	                        toString()));
    18	            }
    19	            
    20	            return;
    21	        }
    22	        
    23	        if (state.equals(LifecycleState.NEW)) {
    24	            init();
    25	        } else if (state.equals(LifecycleState.FAILED)){
    26	            stop();
    27	        } else if (!state.equals(LifecycleState.INITIALIZED) &&
    28	                !state.equals(LifecycleState.STOPPED)) {
    29	            invalidTransition(Lifecycle.BEFORE_START_EVENT);
    30	        }
    31	
    32	        setStateInternal(LifecycleState.STARTING_PREP, null, false);
    33	
    34	        try {
    35	            startInternal();
    36	        } catch (Throwable t) {
    37	            ExceptionUtils.handleThrowable(t);
    38	            setStateInternal(LifecycleState.FAILED, null, false);
    39	            throw new LifecycleException(
    40	                    sm.getString("lifecycleBase.startFail",toString()), t);
    41	        }
    42	
    43	        if (state.equals(LifecycleState.FAILED) ||
    44	                state.equals(LifecycleState.MUST_STOP)) {
    45	            stop();
    46	        } else {
    47	            // Shouldn't be necessary but acts as a check that sub-classes are
    48	            // doing what they are supposed to.
    49	            if (!state.equals(LifecycleState.STARTING)) {
    50	                invalidTransition(Lifecycle.AFTER_START_EVENT);
    51	            }
    52	            
    53	            setStateInternal(LifecycleState.STARTED, null, false);
    54	        }
    55	    }
    56	
    57	
    58	    /**
    59	     * Sub-classes must ensure that the state is changed to
    60	     * {@link LifecycleState#STARTING} during the execution of this method.
    61	     * Changing state will trigger the {@link Lifecycle#START_EVENT} event.
    62	     * 
    63	     * If a component fails to start it may either throw a
    64	     * {@link LifecycleException} which will cause it's parent to fail to start
    65	     * or it can place itself in the error state in which case {@link #stop()}
    66	     * will be called on the failed component but the parent component will
    67	     * continue to start normally.
    68	     * 
    69	     * @throws LifecycleException
    70	     */
    71	    protected abstract void startInternal() throws LifecycleException;
複製代碼

第 7 到 21 行是 start 功能的前置校驗,這裏若是發現 start 方法已經調用過了,將會記錄日誌並直接返回。第 23 到 30 行若是發現 start 放的須要作的前置方法沒有調用完,或者調用出錯,將會先調用這些前置方法。第 32 行暫時先無論,不影響程序閱讀,第 35 行是該方法的實質,將會調用本類中定義的抽象方法 startInternal()(第 71 行)。下面的代碼同上述同樣,都是一些 start 方法調用過程當中可能出現的錯誤的錯誤處理。tomcat

從以上 init 和 start 方法的定義能夠看到這兩個方法最終將會調用子類中定義的 initInternal 和 startInternal 。app

接回本文開頭,一開始在找 StandardServer 類中 init 和 start 方法的定義,看完了上面的分析發現最終會調用 StandardServer 類的 initInternal 和 startInternal 兩個方法。那這兩個方法裏面幹了些什麼?ide

initInternal 方法:ui

1	    /**
     2	     * Invoke a pre-startup initialization. This is used to allow connectors
     3	     * to bind to restricted ports under Unix operating environments.
     4	     */
     5	    @Override
     6	    protected void initInternal() throws LifecycleException {
     7	        
     8	        super.initInternal();
     9	
    10	        // Register global String cache
    11	        // Note although the cache is global, if there are multiple Servers
    12	        // present in the JVM (may happen when embedding) then the same cache
    13	        // will be registered under multiple names
    14	        onameStringCache = register(new StringCache(), "type=StringCache");
    15	
    16	        // Register the MBeanFactory
    17	        MBeanFactory factory = new MBeanFactory();
    18	        factory.setContainer(this);
    19	        onameMBeanFactory = register(factory, "type=MBeanFactory");
    20	        
    21	        // Register the naming resources
    22	        globalNamingResources.init();
    23	        
    24	        // Populate the extension validator with JARs from common and shared
    25	        // class loaders
    26	        if (getCatalina() != null) {
    27	            ClassLoader cl = getCatalina().getParentClassLoader();
    28	            // Walk the class loader hierarchy. Stop at the system class loader.
    29	            // This will add the shared (if present) and common class loaders
    30	            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
    31	                if (cl instanceof URLClassLoader) {
    32	                    URL[] urls = ((URLClassLoader) cl).getURLs();
    33	                    for (URL url : urls) {
    34	                        if (url.getProtocol().equals("file")) {
    35	                            try {
    36	                                File f = new File (url.toURI());
    37	                                if (f.isFile() &&
    38	                                        f.getName().endsWith(".jar")) {
    39	                                    ExtensionValidator.addSystemResource(f);
    40	                                }
    41	                            } catch (URISyntaxException e) {
    42	                                // Ignore
    43	                            } catch (IOException e) {
    44	                                // Ignore
    45	                            }
    46	                        }
    47	                    }
    48	                }
    49	                cl = cl.getParent();
    50	            }
    51	        }
    52	        // Initialize our defined Services
    53	        for (int i = 0; i < services.length; i++) {
    54	            services[i].init();
    55	        }
    56	    }
複製代碼

init 方法裏面作了好幾件事情,牽涉的話題比較多,這裏重點關注最後第 53 到 55 行的代碼,這裏將循環調用 Server 類裏內置的 Service 數組的 init 方法。this

startInternal 方法:url

1	    /**
     2	     * Start nested components ({@link Service}s) and implement the requirements
     3	     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     4	     *
     5	     * @exception LifecycleException if this component detects a fatal error
     6	     *  that prevents this component from being used
     7	     */
     8	    @Override
     9	    protected void startInternal() throws LifecycleException {
    10	
    11	        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    12	        setState(LifecycleState.STARTING);
    13	
    14	        globalNamingResources.start();
    15	        
    16	        // Start our defined Services
    17	        synchronized (services) {
    18	            for (int i = 0; i < services.length; i++) {
    19	                services[i].start();
    20	            }
    21	        }
    22	    }
複製代碼

重點關注第 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 方法:

1	    /**
     2	     * Invoke a pre-startup initialization. This is used to allow connectors
     3	     * to bind to restricted ports under Unix operating environments.
     4	     */
     5	    @Override
     6	    protected void initInternal() throws LifecycleException {
     7	
     8	        super.initInternal();
     9	        
    10	        if (container != null) {
    11	            container.init();
    12	        }
    13	
    14	        // Initialize any Executors
    15	        for (Executor executor : findExecutors()) {
    16	            if (executor instanceof LifecycleMBeanBase) {
    17	                ((LifecycleMBeanBase) executor).setDomain(getDomain());
    18	            }
    19	            executor.init();
    20	        }
    21	
    22	        // Initialize our defined Connectors
    23	        synchronized (connectors) {
    24	            for (Connector connector : connectors) {
    25	                try {
    26	                    connector.init();
    27	                } catch (Exception e) {
    28	                    String message = sm.getString(
    29	                            "standardService.connector.initFailed", connector);
    30	                    log.error(message, e);
    31	
    32	                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
    33	                        throw new LifecycleException(message);
    34	                }
    35	            }
    36	        }
    37	    }
複製代碼

這裏將會調用 Service 下的各種子組件中的 init 方法。

startInternal 方法:

1	    /**
     2	     * Start nested components ({@link Executor}s, {@link Connector}s and
     3	     * {@link Container}s) and implement the requirements of
     4	     * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     5	     *
     6	     * @exception LifecycleException if this component detects a fatal error
     7	     *  that prevents this component from being used
     8	     */
     9	    @Override
    10	    protected void startInternal() throws LifecycleException {
    11	
    12	        if(log.isInfoEnabled())
    13	            log.info(sm.getString("standardService.start.name", this.name));
    14	        setState(LifecycleState.STARTING);
    15	
    16	        // Start our defined Container first
    17	        if (container != null) {
    18	            synchronized (container) {
    19	                container.start();
    20	            }
    21	        }
    22	
    23	        synchronized (executors) {
    24	            for (Executor executor: executors) {
    25	                executor.start();
    26	            }
    27	        }
    28	
    29	        // Start our defined Connectors second
    30	        synchronized (connectors) {
    31	            for (Connector connector: connectors) {
    32	                try {
    33	                    // If it has already failed, don't try and start it
    34	                    if (connector.getState() != LifecycleState.FAILED) {
    35	                        connector.start();
    36	                    }
    37	                } catch (Exception e) {
    38	                    log.error(sm.getString(
    39	                            "standardService.connector.startFailed",
    40	                            connector), e);
    41	                }
    42	            }
    43	        }
    44	    }
複製代碼

同理,將會調用 service 下各種子組件中的 start 方法。

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

1	    protected synchronized void startInternal() throws LifecycleException {
     2	        
     3	        // Log our server identification information
     4	        if(log.isInfoEnabled())
     5	            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
     6	
     7	        // Standard container startup
     8	        super.startInternal();
     9	    }
複製代碼

這裏不一樣於上面兩級容器的實現,而是直接調用了父類的 startInternal 方法:

1	    protected synchronized void startInternal() throws LifecycleException {
     2	
     3	        // Start our subordinate components, if any
     4	        if ((loader != null) && (loader instanceof Lifecycle))
     5	            ((Lifecycle) loader).start();
     6	        logger = null;
     7	        getLogger();
     8	        if ((manager != null) && (manager instanceof Lifecycle))
     9	            ((Lifecycle) manager).start();
    10	        if ((cluster != null) && (cluster instanceof Lifecycle))
    11	            ((Lifecycle) cluster).start();
    12	        Realm realm = getRealmInternal();
    13	        if ((realm != null) && (realm instanceof Lifecycle))
    14	            ((Lifecycle) realm).start();
    15	        if ((resources != null) && (resources instanceof Lifecycle))
    16	            ((Lifecycle) resources).start();
    17	
    18	        // Start our child containers, if any
    19	        Container children[] = findChildren();
    20	        List> results = new ArrayList>();
    21	        for (int i = 0; i < children.length; i++) {
    22	            results.add(startStopExecutor.submit(new StartChild(children[i])));
    23	        }
    24	
    25	        boolean fail = false;
    26	        for (Future result : results) {
    27	            try {
    28	                result.get();
    29	            } catch (Exception e) {
    30	                log.error(sm.getString("containerBase.threadedStartFailed"), e);
    31	                fail = true;
    32	            }
    33	
    34	        }
    35	        if (fail) {
    36	            throw new LifecycleException(
    37	                    sm.getString("containerBase.threadedStartFailed"));
    38	        }
    39	
    40	        // Start the Valves in our pipeline (including the basic), if any
    41	        if (pipeline instanceof Lifecycle)
    42	            ((Lifecycle) pipeline).start();
    43	
    44	
    45	        setState(LifecycleState.STARTING);
    46	
    47	        // Start our thread
    48	        threadStart();
    49	
    50	    }
複製代碼

第 19 到 34 行即啓動當前容器下的子容器的代碼,這裏採用了分線程分別啓動的方式。核心的調用子容器的 start 方法的代碼在 StartChild 類的 call 方法中:

1	    private static class StartChild implements Callable {
     2	
     3	        private Container child;
     4	
     5	        public StartChild(Container child) {
     6	            this.child = child;
     7	        }
     8	
     9	        @Override
    10	        public Void call() throws LifecycleException {
    11	            child.start();
    12	            return null;
    13	        }
    14	    }
複製代碼

這裏使用了JDK 5 的執行線程的方式,不理解的話請參考相關文檔說明。

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 等等組件的這兩個方法都會調用到。

就這樣,Tomcat 7 在內存中爲這一連串組件產生對象,創建對象調用關係,調用它們各自的初始化和啓動方法,啓動的整體過程就介紹完了,這些對象 start 以後將會響應客戶端的請求,爲用戶服務了。固然,這裏尚未涉及到對於具體的發佈到 tomcat 裏面的沒有應用的載入過程,web 應用中配置的 servlet、filter、listener 等的載入、啓動服務過程,瀏覽器發起的一個請求如何通過 Tomcat 內各組件的流轉調用到具體應用裏去的,這一系列問題都還沒談到。由於 Tomcat 自己龐大繁雜,須要找一個視角切入進去,爲了敘述的簡單,先從總體上對 Tomcat 內包含的各組件產生機制有一個大致輪廓的瞭解,這樣爲後面的介紹提供一個統一的背景。

下一篇文章將分析本文開頭遺留的一個問題 —— setStateInternal 方法的做用,以及 Tomcat 中的 Lifecycle 實現原理。

相關文章
相關標籤/搜索