Tomcat 7 啟動分析(五)Lifecycle 機制和實現原理

預流發表於2019-03-03

上篇文章分析 Tomcat 7 的各元件的 init、start 方法時經常會看到有一個 setStateInternal 方法的呼叫,在檢視 LifecycleBase 類及其它各元件的原始碼時會在多處看到這個方法的呼叫,這篇文章就來說說這方法,以及與這個方法相關的 Tomcat 的 Lifecycle 機制和實現原理。

接上文裡談到了 Tomcat 7 的各元件的父類 LifecycleBase 類,該類實現了介面 org.apache.catalina.Lifecycle,下面是這個介面裡定義的常量和方法:

Tomcat 7 啟動分析(五)Lifecycle 機制和實現原理
細心的讀者會發現,上篇文章裡提到的 init 和 start 方法實際上是在這個介面裡面定義好的,也正因為有各元件最終都會實現這個介面作為前提條件,所以才能支援元件內部的 initInternal、startInternal 方法內對於子元件(元件裡面巢狀的子元件都是以介面的形式定義的,但這些介面都會以 Lifecycle 作為父介面)的 init 和 start 方法的呼叫。通過這種方式,只要呼叫了最外層的 Server 元件的 init 和 start 方法,就可以將 Tomcat 內部的各級子元件初始化和啟動起來。我叫這種方式為鏈式呼叫。實際上關於 Tomcat 的關閉機制也是通過這種方式一步步呼叫各層元件的 stop 方法的。這裡不再展開敘述,留待讀者自己研究研究吧。

Lifecycle 介面中的這些字串常量定義主要用於事件型別的定義,先按下不表,文章後面會提到。

重點看下面三個方法:


    /**
     * Add a LifecycleEvent listener to this component.
     *
     * @param listener The listener to add
     */
    public void addLifecycleListener(LifecycleListener listener);//給該組將新增一個監聽器


    /**
     * Get the life cycle listeners associated with this life cycle. If this
     * component has no listeners registered, a zero-length array is returned.
     */
    public LifecycleListener[] findLifecycleListeners();//獲取該元件所有已註冊的監聽器


    /**
     * Remove a LifecycleEvent listener from this component.
     *
     * @param listener The listener to remove
     */
    public void removeLifecycleListener(LifecycleListener listener);//刪除該元件中的一個監聽器
複製程式碼

這三個方法的作用在程式碼的註釋裡簡要說明了一下。這三個方法涉及 org.apache.catalina.LifecycleListener 介面,那麼就看下這個介面的定義:


public interface LifecycleListener {


    /**
     * Acknowledge the occurrence of the specified event.
     *
     * @param event LifecycleEvent that has occurred
     */
    public void lifecycleEvent(LifecycleEvent event);


}
複製程式碼

如此簡單,只有一個方法,這個方法用作某個事件( org.apache.catalina.LifecycleEvent )產生時通知當前監聽器的實現類,具體針對該事件如何處理由監聽器實現類自己決定。

看下 LifecycleEvent 的實現:


public final class LifecycleEvent extends EventObject {

    private static final long serialVersionUID = 1L;


    // ----------------------------------------------------------- Constructors

    /**
     * Construct a new LifecycleEvent with the specified parameters.
     *
     * @param lifecycle Component on which this event occurred
     * @param type Event type (required)
     * @param data Event data (if any)
     */
    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {

        super(lifecycle);
        this.type = type;
        this.data = data;
    }


    // ----------------------------------------------------- Instance Variables


    /**
     * The event data associated with this event.
     */
    private Object data = null;


    /**
     * The event type this instance represents.
     */
    private String type = null;


    // ------------------------------------------------------------- Properties


    /**
     * Return the event data of this event.
     */
    public Object getData() {

        return (this.data);

    }


    /**
     * Return the Lifecycle on which this event occurred.
     */
    public Lifecycle getLifecycle() {

        return (Lifecycle) getSource();

    }


    /**
     * Return the event type of this event.
     */
    public String getType() {

        return (this.type);

    }


}
複製程式碼

這個類也很簡單,data 和 type 作為類的內建例項變數,唯一特別是使用了 jdk 內建的 java.util.EventObject 作為父類來支援事件定義,這裡在事件建構函式中將 org.apache.catalina.Lifecycle 類的例項 lifecycle 作為事件源,儲存 lifecycle 物件的引用,並提供了 getLifecycle 方法返回這個引用。

那麼 Tomcat 中是如何實現關於這些事件的監聽以及通知的呢?

在本文開頭提到 的LifecycleBase 類中第 47 行定義了一個例項變數 lifecycle ,正是通過該變數來註冊元件上定義的各類監聽器的。留心一下 lifecycle 這個例項變數,它並不是 org.apache.catalina.Lifecycle 類的例項,而是 org.apache.catalina.util.LifecycleSupport 類的例項。正是這個工具類提供了事件監聽和事件通知的功能。

先看下實際程式碼中是如何給元件釋出時間通知的,看下前面文章中曾經提到過的 org.apache.catalina.core.StandardServer 類的 startInternal 方法:


     1	    protected void startInternal() throws LifecycleException {
     2	
     3	        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
     4	        setState(LifecycleState.STARTING);
     5	
     6	        globalNamingResources.start();
     7	        
     8	        // Start our defined Services
     9	        synchronized (services) {
    10	            for (int i = 0; i < services.length; i++) {
    11	                services[i].start();
    12	            }
    13	        }
    14	    }
複製程式碼

我們前面已經分析過第 9 到 13 行程式碼,這裡看下第 3 行,它呼叫了父類 org.apache.catalina.util.LifecycleBase 裡的 fireLifecycleEvent 方法,這裡的CONFIGURE_START_EVENT就是本文最開始 Lifecycle 介面中定義的常量,這裡表示釋出了一個 start 配置事件。

org.apache.catalina.util.LifecycleBase 類中的 fireLifecycleEvent 方法裡呼叫的是 org.apache.catalina.util.LifecycleSupport 類 fireLifecycleEvent 方法,該方法程式碼如下:


    public void fireLifecycleEvent(String type, Object data) {

        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);

    }
複製程式碼

通過傳進來的兩個引數構造一個 LifecycleEvent 物件,然後向註冊到元件中的所有監聽器釋出這個新構造的事件物件。

這裡有個疑問,到底什麼時候向元件裡註冊監聽器的呢?

還是以 StandardServer 舉例,在前面講 Digester 的使用時,org.apache.catalina.startup.Catalina 類的 createStartDigester 方法有這麼一段程式碼:


     1	        // Configure the actions we will be using
     2	        digester.addObjectCreate("Server",
     3	                                 "org.apache.catalina.core.StandardServer",
     4	                                 "className");
     5	        digester.addSetProperties("Server");
     6	        digester.addSetNext("Server",
     7	                            "setServer",
     8	                            "org.apache.catalina.Server");
     9	
    10	        digester.addObjectCreate("Server/GlobalNamingResources",
    11	                                 "org.apache.catalina.deploy.NamingResources");
    12	        digester.addSetProperties("Server/GlobalNamingResources");
    13	        digester.addSetNext("Server/GlobalNamingResources",
    14	                            "setGlobalNamingResources",
    15	                            "org.apache.catalina.deploy.NamingResources");
    16	
    17	        digester.addObjectCreate("Server/Listener",
    18	                                 null, // MUST be specified in the element
    19	                                 "className");
    20	        digester.addSetProperties("Server/Listener");
    21	        digester.addSetNext("Server/Listener",
    22	                            "addLifecycleListener",
    23	                            "org.apache.catalina.LifecycleListener");
複製程式碼

第 17 到 24 行,將呼叫 org.apache.catalina.core.StandardServer 類的 addLifecycleListener 方法,將根據 server.xml 中配置的 Server 節點下的 Listener 節點所定義的 className 屬性構造物件例項,並作為 addLifecycleListener 方法的入參。所有的監聽器都會實現上面提到的 org.apache.catalina.LifecycleListener 介面。Server 節點下的 Listener 節點有好幾個,這裡以 org.apache.catalina.core.JasperListener 舉例。

在構造完 org.apache.catalina.core.JasperListener 類的物件之後,呼叫 addLifecycleListener 方法,這個方法並沒有直接在 org.apache.catalina.core.StandardServer 類中定義,而是在它的父類 org.apache.catalina.util.LifecycleBase 中:


    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycle.addLifecycleListener(listener);
    }
複製程式碼

這裡呼叫的是前述的 org.apache.catalina.util.LifecycleSupport 類的 addLifecycleListener 方法:


    /**
     * Add a lifecycle event listener to this component.
     *
     * @param listener The listener to add
     */
    public void addLifecycleListener(LifecycleListener listener) {

      synchronized (listenersLock) {
          LifecycleListener results[] =
            new LifecycleListener[listeners.length + 1];
          for (int i = 0; i < listeners.length; i++)
              results[i] = listeners[i];
          results[listeners.length] = listener;
          listeners = results;
      }

    }
複製程式碼

LifecycleSupport 作為一個工具類,內部儲存了一個監聽器物件例項陣列,見該類的第 68 行:


/** 
 * The set of registered LifecycleListeners for event notifications. 
 */  
private LifecycleListener listeners[] = new LifecycleListener[0];  
複製程式碼

上面的 addLifecycleListener 方法內部實現的是同步給該陣列增加一個監聽器物件。

看到這裡應該大體明白 Tomcat 中的 Lifecycle 是怎麼回事了,總的來說就是通過一個工具類 LifecycleSupport ,呼叫該類的 addLifecycleListener 方法增加監聽器,需要釋出事件時還是呼叫該工具類的 fireLifecycleEvent 方法,將事件釋出給元件上註冊的所有監聽器,由監聽器內部實現來決定是否處理該事件。

拿前面看到的一個監聽器 org.apache.catalina.core.JasperListener 舉例:


     1	public class JasperListener
     2	    implements LifecycleListener {
     3	
     4	    private static final Log log = LogFactory.getLog(JasperListener.class);
     5	
     6	    /**
     7	     * The string manager for this package.
     8	     */
     9	    protected static final StringManager sm =
    10	        StringManager.getManager(Constants.Package);
    11	
    12	
    13	    // ---------------------------------------------- LifecycleListener Methods
    14	
    15	
    16	    /**
    17	     * Primary entry point for startup and shutdown events.
    18	     *
    19	     * @param event The event that has occurred
    20	     */
    21	    @Override
    22	    public void lifecycleEvent(LifecycleEvent event) {
    23	
    24	        if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) {
    25	            try {
    26	                // Set JSP factory
    27	                Class.forName("org.apache.jasper.compiler.JspRuntimeContext",
    28	                              true,
    29	                              this.getClass().getClassLoader());
    30	            } catch (Throwable t) {
    31	                ExceptionUtils.handleThrowable(t);
    32	                // Should not occur, obviously
    33	                log.warn("Couldn't initialize Jasper", t);
    34	            }
    35	            // Another possibility is to do directly:
    36	            // JspFactory.setDefaultFactory(new JspFactoryImpl());
    37	        }
    38	
    39	    }
    40	
    41	
    42	}
複製程式碼

重點關注來自介面的 lifecycleEvent 方法的實現,可以看到這個監聽器只關心事件型別為BEFORE_INIT_EVENT的事件,如果釋出了該事件,才會做後續處理(這裡會產生一個 org.apache.jasper.compiler.JspRuntimeContext 物件)。

Lifecycle 相關類 UML 關係圖:

Tomcat 7 啟動分析(五)Lifecycle 機制和實現原理
如果對設計模式比較熟悉的話會發現 Tomcat 的 Lifecycle 使用的是觀察者模式:LifecycleListener 代表的是抽象觀察者,它定義一個 lifecycleEvent 方法,而實現該介面的監聽器是作為具體的觀察者。Lifecycle 介面代表的是抽象主題,它定義了管理觀察者的方法和它要所做的其它方法。而各元件代表的是具體主題,它實現了抽象主題的所有方法。通常會由具體主題儲存對具體觀察者物件有用的內部狀態;在這種內部狀態改變時給其觀察者發出一個通知。Tomcat 對這種模式做了改進,增加了另外兩個工具類:LifecycleSupport、LifecycleEvent ,它們作為輔助類擴充套件了觀察者的功能。LifecycleEvent 中定義了事件類別,不同的事件在具體觀察者中可區別處理,更加靈活。LifecycleSupport 類代理了所有具體主題對觀察者的管理,將這個管理抽出來統一實現,以後如果修改只要修改 LifecycleSupport 類就可以了,不需要去修改所有具體主題,因為所有具體主題的對觀察者的操作都被代理給 LifecycleSupport 類了。

事件的釋出使用的是推模式,即每釋出一個事件都會通知主題的所有具體觀察者,由各觀察者再來決定是否需要對該事件進行後續處理。

下面再來看看本文一開頭所說的 setStateInternal 方法,以 org.apache.catalina.core.StandardServer 類為例,上面看到的 startInternal 方法中第 4 行:setState(LifecycleState.STARTING);

它呼叫了父類 org.apache.catalina.util.LifecycleBase 中的 setState 方法:


    /**
     * Provides a mechanism for sub-classes to update the component state.
     * Calling this method will automatically fire any associated
     * {@link Lifecycle} event. It will also check that any attempted state
     * transition is valid for a sub-class.
     * 
     * @param state The new state for this component
     */
    protected synchronized void setState(LifecycleState state)
            throws LifecycleException {
        setStateInternal(state, null, true);
    }
複製程式碼

在這個類裡面呼叫本類的一個同步方法 setStateInternal :


     1	    private synchronized void setStateInternal(LifecycleState state,
     2	            Object data, boolean check) throws LifecycleException {
     3	        
     4	        if (log.isDebugEnabled()) {
     5	            log.debug(sm.getString("lifecycleBase.setState", this, state));
     6	        }
     7	        
     8	        if (check) {
     9	            // Must have been triggered by one of the abstract methods (assume
    10	            // code in this class is correct)
    11	            // null is never a valid state
    12	            if (state == null) {
    13	                invalidTransition("null");
    14	                // Unreachable code - here to stop eclipse complaining about
    15	                // a possible NPE further down the method
    16	                return;
    17	            }
    18	            
    19	            // Any method can transition to failed
    20	            // startInternal() permits STARTING_PREP to STARTING
    21	            // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
    22	            // STOPPING
    23	            if (!(state == LifecycleState.FAILED ||
    24	                    (this.state == LifecycleState.STARTING_PREP &&
    25	                            state == LifecycleState.STARTING) ||
    26	                    (this.state == LifecycleState.STOPPING_PREP &&
    27	                            state == LifecycleState.STOPPING) ||
    28	                    (this.state == LifecycleState.FAILED &&
    29	                            state == LifecycleState.STOPPING))) {
    30	                // No other transition permitted
    31	                invalidTransition(state.name());
    32	            }
    33	        }
    34	        
    35	        this.state = state;
    36	        String lifecycleEvent = state.getLifecycleEvent();
    37	        if (lifecycleEvent != null) {
    38	            fireLifecycleEvent(lifecycleEvent, data);
    39	        }
    40	    }
複製程式碼

重點關注第 35 到 39 行,第 35 行將入參 LifecycleState 例項賦值給本類中的例項變數儲存起來,第 36 行取出 LifecycleState 例項的 LifecycleEvent 事件,如果該事件非空,則呼叫 fireLifecycleEvent 方法釋出該事件。

既然看到了 LifecycleState 類,就看下 LifecycleState 類的定義:


     1	public enum LifecycleState {
     2	    NEW(false, null),
     3	    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
     4	    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
     5	    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
     6	    STARTING(true, Lifecycle.START_EVENT),
     7	    STARTED(true, Lifecycle.AFTER_START_EVENT),
     8	    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
     9	    STOPPING(false, Lifecycle.STOP_EVENT),
    10	    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    11	    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    12	    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    13	    FAILED(false, null),
    14	    MUST_STOP(true, null),
    15	    MUST_DESTROY(false, null);
    16	
    17	    private final boolean available;
    18	    private final String lifecycleEvent;
    19	
    20	    private LifecycleState(boolean available, String lifecycleEvent) {
    21	        this.available = available;
    22	        this.lifecycleEvent = lifecycleEvent;
    23	    }
    24	
    25	    /**
    26	     * May the public methods other than property getters/setters and lifecycle
    27	     * methods be called for a component in this state? It returns
    28	     * <code>true</code> for any component in any of the following states:
    29	     * <ul>
    30	     * <li>{@link #STARTING}</li>
    31	     * <li>{@link #STARTED}</li>
    32	     * <li>{@link #STOPPING_PREP}</li>
    33	     * <li>{@link #MUST_STOP}</li>
    34	     * </ul>
    35	     */
    36	    public boolean isAvailable() {
    37	        return available;
    38	    }
    39	
    40	    /**
    41	     *
    42	     */
    43	    public String getLifecycleEvent() {
    44	        return lifecycleEvent;
    45	    }
    46	}
複製程式碼

這個類在之前的 Tomcat 4 和 Tomcat 5 中都沒有看到,可能是 Tomcat 7 裡面新定義的吧,就是一個列舉,內嵌了兩個例項變數,一個布林值表示是否可用,一個字串表示是事件型別,看已經定義的列舉值裡面發現這個字串要麼不設值,要麼就是 Lifecycle 類中定義好的字串常量。這個類實際上就是對 Lifecycle 類中定義好的字串常量做了另外一層封裝。

再說回開頭在各元件程式碼中經常會看到的 setStateInternal 方法的呼叫,實際上就是向該元件中已註冊的監聽器釋出一個事件。

相關文章