關於Tomcat的觀察者模式-推薦好文

文章寫得很詳細,容易理解,轉載推薦。
原文地址:http://www.cnblogs.com/chenying99/archive/2012/09/05/2671199.html

轉載主要是供自己記錄學習用的,哈哈哈~~~~

本文主要結合觀察者模式,講述Tomcat的生命週期管理。Tomcat的生命週期管理機制設計的非常優雅,在Tomcat啓動時,只需要啓動一個Server組件,就會啓動所有的容器及對應的組件,並且觸發這些容器的監聽者,完成啓動過程的設置。可以說是“一鍵式”啓動的。停止過程也是一樣。

本文首先簡單介紹Tomcat中容器,組件及監聽類的功能。因爲Tomcat的生命週期管理應用了觀察者模式,所以接下來會分析觀察者模式,最後結合觀察者模式及Tomcat源代碼,詳細說明Tomcat的生命週期管理。

一、幾種基本的容器,組件及事件監聽類(Listener)

  1. Tomcat中有四種不同的容器:
    Engine:代表整個Catalina servle引擎
    Host:代表虛擬主機
    Context:代表某個web應用
    Wrapper:代表某個應用中的servlet
    這些容器都擴展了Container接口(譯爲:容器,這也是爲什麼一般都稱tomcat爲容器而不是服務器的原因之一吧~)。更重要的是,這些容器都是父子的關係,Engine位於最頂層,一個Engine包含多個Host,一個Host(虛擬主機)包含多個Context(web應用),一個Context(web 應用)包含多個Wrapper(servlet),Wrapper位於最底層,沒有孩子。當父容器啓動時,相應的子容器也應該啓動,子容器的子容器也啓動。如此,只需要啓動最頂層的容器,就會啓動所有的子容器。

  2. Tomcat的容器中有很多組件,如:
    Logger:負責記錄各種事件
    Loader:負責加載類文件,如加載應用程序中的Servlet
    Manager:負責管理session
    Realm: 負責用戶驗證與授權
    Pipeline:負責完成容器invoke方法的調用,對請求進行處理(責任鏈模式的經典應用)。
    當tomcat容器啓動時,這些組件也要啓動,進行初始化。當容器停止時,這些組件也應該停止,進行相應的清理工作。比如管理用戶session的Manager組件,在tomcat停止時,會將這些session序列化到sessions.ser文件中(位於%tomcat_home%/work/web appcation/sessions.ser)。下一次啓動tomcat時,manager會將這個文件中的session反序列化到內存,將刪除sessions.ser文件。這也是爲什麼重啓tomcat時,正在訪問網站的用戶不用重新登錄的原因。

  3. 另外,還有一些類,它們並不像容器的基本組件(如Logger, Loader, Manager)一樣,爲容器的整個生命週期所調用,而僅僅對容器的某幾個特定事件感興趣。如:
    ContextConfig: Context的收聽者,在Context(web 應用)啓動時,ContextConfig對web應用程序的配置文件web.xml進行分析,爲Context生成Wrapper等對象,並與Context關聯。在Context停止時,爲Context清除這些關聯的對象。
    HostConfig: Host的收聽者,在Host(虛擬主機)啓動時,HostConfig會自動的爲Host部署放置在webapps中的web應用程序。在Host停止時,爲Host清除這些關聯的對象。
    EngineConfig:Engine的收聽者,這個對比較簡單,僅僅是在Engine啓動與停止時做一些簡單的記錄。
    這些監聽類如何監聽容器的特定事件呢?如何在特定事件發現時,調用監聽類的特定方法以完成某些設置呢?如果理解了觀察者模式,便能輕易的理解Tomcat的整個生命週期管理了。

二、觀察者模式:

觀察者模式又叫做發佈-訂閱(Publish/Subscribe)模式、源-監聽(Source/Listener)模式。它定義了一種一對多的依賴關係,一個主題,多個觀察者,當主題發生變化的時候,會主動的通知觀察者,這樣觀察者便能針對主題發生的變化,執行某些對應的動作。觀察者模式的應用非常廣泛,如Java AWT事件模型,Servlet的監聽器,Spring事件處理機制以及本文所講述的Tomcat生命週期管理機制等等;應用如此的廣泛,以至於Java直接提供API的支持,java.util.Observable和java.util.Observer;

觀察者模式的結構:
這裏寫圖片描述
觀察者模式

觀察者模式包括以下角色:

抽象主題(Observable):定義了管理觀察者的添加,刪除和通知方法。

抽象觀察者(Observer):定義了主題發生變化時,具體觀察者必須執行的方法。

具體主題(Container):對觀察者進行管理,並在自身發生變化時,通知觀察者。

具體觀察者(ContainerConfig):實現了當主題發生變化時,應該執行的動作。

下面是觀察者模式的示例代碼:

package com.scnulh.observer;

抽象主題:

/** 
 * 抽象主題 
 */  

public interface Observable {  

    //添加觀察者  
    void addObserver(Observer observer);  

    //刪除觀察者  
    void deleteObserver(Observer observer);  

    //在事件發生時,通知觀察者  
    void notifyObservers();  
}  

抽象觀察者:

package com.scnulh.observer;  

/** 

 * 抽象觀察者 

 */  

public interface Observer {  

    /** 

     * 更新方法,觀察者根據傳入的主題對象獲取主題的上下文 

     * 根據傳入的Object對象判斷髮生了何種事件 

     * @param observable 

     * @param arg 

     */  

    void update(Observable observable,Object arg);  
}  

具體主題:

package com.scnulh.observer;  

import java.util.ArrayList;  
import java.util.List;  

/** 
 * 具體主題,假設這是一個Tomcat的容器基類  
 * 有一個start方法,代表容器的啓動,在啓動過程中 
 * 通知所有觀察者  
 */  

public class ContainerBase implements Observable{  

    //持有觀察者的List  

    private List<Observer> observers=new ArrayList<Observer>();   

    //添加觀察者  

    @Override  

    public void addObserver(Observer observer) {  

       observers.add(observer);  

    }  
    //刪除觀察者  

    @Override  

    public void deleteObserver(Observer observer) {  

       observers.remove(observer);  

    }  

    //通知所有觀察者  

    @Override  

    public void notifyObservers() {  

       for(Observer observer:observers)  

       {  

           observer.update(this, "start");  
       }  
    }   

    //容器的啓動方法,啓動容器並調用notifyObservers方法通知所有觀察者  

    public void start()  

    {  
       System.out.println("container start");  
       notifyObservers();  
    }  

    public static void main(String[] args) {    

       ContainerBase container=new ContainerBase();//聲明一個容器  

       Observer observer=new ContainerConfig();    //聲明一個監聽類  

       container.addObserver(observer);            //爲容器添加監聽類  

       container.start();                          //啓動容器  
    }  
}  

具體觀察者:

package com.scnulh.observer;  

/** 
 * 具體觀察者,假設這是一個容器的監聽類, 
 * 在tomcat容器啓動時,處理tomcat的配置 
 */  

public class ContainerConfig implements Observer{  

    @Override  

    public void update(Observable observable, Object arg) {  

       String event=(String) arg;  
       if(event.equals("start"))  
       {  
           System.out.println("container starting, do container configs");  
       }  
    }  
}  

上述便是觀察者模式的簡單示例,之所以用ContainerBase和ContainerConfig作爲具體主題和觀察者,是因爲後面要分析tomcat的容器(Container)和監聽類(Config)的源代碼,這裏先模擬下他們的工作方式。

細心的讀者很快就會發現,在具體主題ContainerBaser中,對觀察者的管理方法其實是很固定的,無非就是聲明一個Observer的集合,提供添加,刪除,查找的方法。甚至連在主題發生變化時,通知觀察者的方法也是固定的,即輪循的通知每一個觀察者。如果每一個實現了主題接口的具體主題都要實現這些方法,無疑會造成重複,帶來代碼編寫上的麻煩。爲了消除重複,減少麻煩,可以提供一個類,實現主題對觀察者的管理及通知。這正是java util包裏Observable與Observer所做的。感興趣的讀者可以出看看,這裏就不貼代碼了。Tomcat沒有直接使用這個Observable類,而是另外實現了一個LifecycleSupport類。

總的來說,觀察者模式還是很好理解的,要讓觀察者模式用於實際,關鍵有兩點,一點要提供主題與觀察者的實現,第二是將觀察者註冊到具體主題中,這樣主題發生變化時,才能通知到觀察者。

三、Tomcat生命週期管理

理解了觀察者模式,Tomcat的生命週期管理便很容易理解了。所涉及的類有:

Lifecycle:相當於抽象主題角色,所有的容器類與組件實現類都實現了這個接口。如StandardContext
LifecycleListener:相當於抽象觀察者角色,具體的實現類有ContextConfig, HostConfig, EngineConfig類,它們在容器啓動時與停止時觸發。
LifecycleEvent:生命週期事件,對主題與發生的事件進行封裝。
LifecycleSupport:生命週期管理的實用類,提供對觀察者的添加,刪除及通知觀察者的方法。
LifecycleException:生命週期異常類。

Lifecycle接口

package com.apache.catalina;  
import org.apache.catalina.LifecycleException;  

public interface Lifecycle {      

    //生命週期內的六個事件  

    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 void addLifecycleListener(LifecycleListener listener);  

    public void removeLifecycleListener(LifecycleListener listener);  

    public LifecycleListener[] findLifecycleListeners();  

    //主題的啓動與停止方法  

    public void start()throws LifecycleException;  

    public void stop()throws LifecycleException;  

}  

Lifecycle相當於觀察者模式中的抽象主題角色(Observable),它定義了添加、刪除及通知管理者的方法。

還定義了與生命週期相關的6個事件。Start和stop方法是Lifecycle最重要的兩個方法,分別代表啓動與停止。所有四種容器的標準實現類(StandardEngine, StandardHost, StandardContext,StandardWrapper)和基本組件(Logger,Loader,Manager等)的實現類都實現了Lifecycle接口,這意義着它們都是具體的觀察者,具有啓動和停止方法。容器啓動時,主要做三件事:調用組件的啓動方法,啓動組件;調用子容器的啓動方法,啓動子容器;通知容器的觀察者,使其執行相應的啓動動作。子容器啓動也做這三件事,這樣整個Tomcat便啓動了。Tomcat的停止也類似。

LifecycleListener接口:

package org.apache.catalina;  

public interface LifecycleListener {  
    /** 
     * Acknowledge the occurrence of the specified event. 
     * @param event LifecycleEvent that has occurred 
     */  

    public void lifecycleEvent(LifecycleEvent event);  
}  

LifecycleListener相當於觀察者模式中的抽象觀察者角色(Observer),可以看到它與Observer非常的類似,都只有一個更新自己的方法。不同的是,Observer 更新方法中,有兩個參數:Observable與Object,而LifecycleListener中只有一個參數LifecycleEvent,這正是對前面兩個參數的封裝。

LifecycleEvent:

package org.apache.catalina;  
import java.util.EventObject;  

public final class LifecycleEvent  
    extends EventObject {  

    public LifecycleEvent(Lifecycle lifecycle, String type) {  

        this(lifecycle, type, null);  
    }  

    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {  

        super(lifecycle);  

        this.lifecycle = lifecycle;  

        this.type = type;  

        this.data = data;  

    }  

    private Object data = null;      

    private Lifecycle lifecycle = null;      

    private String type = null;       

    public Object getData() {  

        return (this.data);  

    }  

    public Lifecycle getLifecycle() {  

        return (this.lifecycle);  

    }  

    public String getType() {  

        return (this.type);  

    }  
} 

LifecycleEvent

是對主題(事件源),事件及相關數據的封裝,繼承自java.util.

是對主題(事件源),事件及相關數據的封裝,繼承自java.util.

EventObject.

LifecycleSupport:

前面說過,抽象主題定義的添加,刪除和通知觀察者的方法都是很固定的,每個實現類實現起來都一樣,這樣就可以提供一個類來實現這些功能,具體的主題類直接調用便可以了。

package org.apache.catalina.util;  

import org.apache.catalina.Lifecycle;  

import org.apache.catalina.LifecycleEvent;  

import org.apache.catalina.LifecycleListener;  

public final class LifecycleSupport {  

    public LifecycleSupport(Lifecycle lifecycle) {  

        super();  

        this.lifecycle = lifecycle;  
    }  

    private Lifecycle lifecycle = null;    

    private LifecycleListener listeners[] = new LifecycleListener[0];  

    //添加一個觀察者  

    public void addLifecycleListener(LifecycleListener listener) {  

      synchronized (listeners) {  

          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;  

      }  

    }  

    //找出所註冊的觀察者  

    public LifecycleListener[] findLifecycleListeners() {  

        return listeners;  
    }  

    //通知觀察者  

    public void fireLifecycleEvent(String type, Object data) {  

        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);  

        LifecycleListener interested[] = null;  

        synchronized (listeners) {  

            interested = (LifecycleListener[]) listeners.clone();  

        }  

        for (int i = 0; i < interested.length; i++)  

            interested[i].lifecycleEvent(event);  
    }  

    //刪除一個觀察者  

    public void removeLifecycleListener(LifecycleListener listener) {  

        synchronized (listeners) {  

            int n = -1;  

            for (int i = 0; i < listeners.length; i++) {  

                if (listeners[i] == listener) {  

                    n = i;  

                    break;  

                }  

            }  

            if (n < 0)  

                return;  

            LifecycleListener results[] =  

              new LifecycleListener[listeners.length - 1];  

            int j = 0;  

            for (int i = 0; i < listeners.length; i++) {  

                if (i != n)  

                    results[j++] = listeners[i];  

            }  

            listeners = results;  

        }  
    }  

}  

這樣,具體的主題實現抽象主題中對觀察者的添加、刪除與通知方法便非常簡單了。

如在ContainerBase(容器的基本實現類)中:

protected LifecycleSupport lifecycle = new LifecycleSupport(this);  

    public void addLifecycleListener(LifecycleListener listener) {  

        lifecycle.addLifecycleListener(listener);  
    }  

    public LifecycleListener[] findLifecycleListeners() {  

        return lifecycle.findLifecycleListeners();    
}  

    public void removeLifecycleListener(LifecycleListener listener) {  

        lifecycle.removeLifecycleListener(listener);  
}  

Lifecycle,LifecycleListener,LifecycleSupport,LifecycleEvent, LifecycleException及其具體的觀察者與具體的主題之間的關係如下:
這裏寫圖片描述
結構圖

讓我們再來看一下StandardContext的啓動方法,StandartContext實現了Lifecycle,它的啓動方法由上一級容器所調用。

public synchronized void start() throws LifecycleException {

//略過N多代碼



//通知觀察者,容器即將啓動

lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);



//略過N多代碼

// 啓動loader,cluster,realm組件

if ((loader != null) && (loader instanceof Lifecycle))

         ((Lifecycle) loader).start();

if ((cluster != null) && (cluster instanceof Lifecycle))

         ((Lifecycle) cluster).start();

if ((realm != null) && (realm instanceof Lifecycle))

         ((Lifecycle) realm).start();

//略過N多代碼



//找出所有的子容器,並且啓動

Container children[] = findChildren();

for (int i = 0; i < children.length; i++) {

      if (children[i] instanceof Lifecycle)

            ((Lifecycle) children[i]).start();

  }



//通知所有觀察者,容器正在啓動

lifecycle.fireLifecycleEvent(START_EVENT, null);



//啓動Manager組件

if ((manager != null) && (manager instanceof Lifecycle))

         ((Lifecycle) manager).start();

//通知所有觀察者,容器已經啓動

lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);



}

這裏主要做三件事:調用組件的啓動方法,啓動組件;調用子容器的啓動方法,啓動子容器;通知容器的觀察者,使其執行相應的啓動動作。每一層次的容器都這樣啓動,最終整個Tomcat啓動完畢。

因爲源代碼實在是太多了,沒法全部貼出來,如果感興趣,大家可以在附件上下下來研究。

推薦大家閱讀How Tomcat Works這本書。就像書名一樣,這本書詳細的剖析了Tomcat運作機制,寫的非常的好,在豆瓣這本書的評分是9.4分,而同樣經典的Thinking in Java爲9.1分,Effective Java爲9.2分。Tomcat的源代碼非常值得研究,裏面用了很多的設計模式,如本文講的觀察者模式,還是上一篇所講的單例模式,以及門面模式,責任鏈模式等等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章