Tomcat生命周期管理與觀察者模式


本文主要結合觀察者模式,講述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;

抽象主題:

Java代碼    收藏代碼
  1. /** 
  2.  
  3.  * 抽象主題 
  4.  
  5.  */  
  6.   
  7. public interface Observable {  
  8.   
  9.       
  10.   
  11.     //添加觀察者  
  12.   
  13.     void addObserver(Observer observer);  
  14.   
  15.     //刪除觀察者  
  16.   
  17.     void deleteObserver(Observer observer);  
  18.   
  19.     //在事件發生時,通知觀察者  
  20.   
  21.     void notifyObservers();  
  22.   
  23. }  

 

 

抽象觀察者:

Java代碼    收藏代碼
  1. package com.scnulh.observer;  
  2.   
  3.    
  4.   
  5. /** 
  6.  
  7.  * 抽象觀察者 
  8.  
  9.  */  
  10.   
  11. public interface Observer {  
  12.   
  13.       
  14.   
  15.     /** 
  16.  
  17.      * 更新方法,觀察者根據傳入的主題對象獲取主題的上下文 
  18.  
  19.      * 根據傳入的Object對象判斷發生了何種事件 
  20.  
  21.      * @param observable 
  22.  
  23.      * @param arg 
  24.  
  25.      */  
  26.   
  27.     void update(Observable observable,Object arg);  
  28.   
  29.    
  30.   
  31. }  

 

 

 

具體主題:

Java代碼    收藏代碼
  1. package com.scnulh.observer;  
  2.   
  3.    
  4.   
  5. import java.util.ArrayList;  
  6.   
  7. import java.util.List;  
  8.   
  9.    
  10.   
  11. /** 
  12.  
  13.  * 具體主題,假設這是一個Tomcat的容器基類 
  14.  
  15.  * 有一個start方法,代表容器的啟動,在啟動過程中 
  16.  
  17.  * 通知所有觀察者  
  18.  
  19.  */  
  20.   
  21. public class ContainerBase implements Observable{  
  22.   
  23.    
  24.   
  25.     //持有觀察者的List  
  26.   
  27.     private List<Observer> observers=new ArrayList<Observer>();   
  28.   
  29.    
  30.   
  31.    
  32.   
  33.     //添加觀察者  
  34.   
  35.     @Override  
  36.   
  37.     public void addObserver(Observer observer) {  
  38.   
  39.        observers.add(observer);  
  40.   
  41.     }  
  42.   
  43.    
  44.   
  45.     //刪除觀察者  
  46.   
  47.     @Override  
  48.   
  49.     public void deleteObserver(Observer observer) {  
  50.   
  51.        observers.remove(observer);  
  52.   
  53.     }  
  54.   
  55.    
  56.   
  57.     //通知所有觀察者  
  58.   
  59.     @Override  
  60.   
  61.     public void notifyObservers() {  
  62.   
  63.        for(Observer observer:observers)  
  64.   
  65.        {  
  66.   
  67.            observer.update(this, "start");  
  68.   
  69.        }  
  70.   
  71.     }  
  72.   
  73.       
  74.   
  75.     //容器的啟動方法,啟動容器並調用notifyObservers方法通知所有觀察者  
  76.   
  77.     public void start()  
  78.   
  79.     {  
  80.   
  81.        System.out.println("container start");  
  82.   
  83.        notifyObservers();  
  84.   
  85.     }  
  86.   
  87.       
  88.   
  89.     public static void main(String[] args) {  
  90.   
  91.          
  92.   
  93.        ContainerBase container=new ContainerBase();//聲明一個容器  
  94.   
  95.        Observer observer=new ContainerConfig();    //聲明一個監聽類  
  96.   
  97.        container.addObserver(observer);            //為容器添加監聽類  
  98.   
  99.        container.start();                          //啟動容器  
  100.   
  101.    
  102.   
  103.     }  
  104.   
  105.    
  106.   
  107. }  

  

 

 

 

具體觀察者:

Java代碼    收藏代碼
  1. package com.scnulh.observer;  
  2.   
  3.    
  4.   
  5. /** 
  6.  
  7.  * 具體觀察者,假設這是一個容器的監聽類, 
  8.  
  9.  * 在tomcat容器啟動時,處理tomcat的配置 
  10.  
  11.  */  
  12.   
  13. public class ContainerConfig implements Observer{  
  14.   
  15.    
  16.   
  17.     @Override  
  18.   
  19.     public void update(Observable observable, Object arg) {  
  20.   
  21.        String event=(String) arg;  
  22.   
  23.        if(event.equals("start"))  
  24.   
  25.        {  
  26.   
  27.            System.out.println("container starting, do container configs");  
  28.   
  29.        }  
  30.   
  31.     }  
  32.   
  33. }  

  

 

 

         上述便是觀察者模式的簡單示例,之所以用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接口

Java代碼    收藏代碼
  1. package com.apache.catalina;  
  2.   
  3.    
  4.   
  5. import org.apache.catalina.LifecycleException;  
  6.   
  7.    
  8.   
  9. public interface Lifecycle {      
  10.   
  11.     //生命周期內的六個事件  
  12.   
  13.     public static final String START_EVENT = "start";     
  14.   
  15.     public static final String BEFORE_START_EVENT = "before_start";  
  16.   
  17.     public static final String AFTER_START_EVENT = "after_start";     
  18.   
  19.     public static final String STOP_EVENT = "stop";     
  20.   
  21.     public static final String BEFORE_STOP_EVENT = "before_stop";      
  22.   
  23.     public static final String AFTER_STOP_EVENT = "after_stop";  
  24.   
  25.     //觀察者的管理與通知方法  
  26.   
  27.     public void addLifecycleListener(LifecycleListener listener);  
  28.   
  29.     public void removeLifecycleListener(LifecycleListener listener);  
  30.   
  31.     public LifecycleListener[] findLifecycleListeners();  
  32.   
  33.     //主題的啟動與停止方法  
  34.   
  35.     public void start()throws LifecycleException;  
  36.   
  37.     public void stop()throws LifecycleException;  
  38.   
  39. }  

 

 

 

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

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

 

LifecycleListener接口:

Java代碼    收藏代碼
  1. package org.apache.catalina;  
  2.   
  3. public interface LifecycleListener {  
  4.   
  5.     /** 
  6.  
  7.      * Acknowledge the occurrence of the specified event. 
  8.  
  9.      * 
  10.  
  11.      * @param event LifecycleEvent that has occurred 
  12.  
  13.      */  
  14.   
  15.     public void lifecycleEvent(LifecycleEvent event);  
  16.   
  17.    
  18.   
  19. }  

 

 

 

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

 

LifecycleEvent:

Java代碼    收藏代碼
  1. package org.apache.catalina;  
  2.   
  3.    
  4.   
  5. import java.util.EventObject;  
  6.   
  7.    
  8.   
  9. public final class LifecycleEvent  
  10.   
  11.     extends EventObject {  
  12.   
  13.       
  14.   
  15.     public LifecycleEvent(Lifecycle lifecycle, String type) {  
  16.   
  17.         this(lifecycle, type, null);  
  18.   
  19.     }  
  20.   
  21.    
  22.   
  23.     public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {  
  24.   
  25.         super(lifecycle);  
  26.   
  27.         this.lifecycle = lifecycle;  
  28.   
  29.         this.type = type;  
  30.   
  31.         this.data = data;  
  32.   
  33.     }  
  34.   
  35.    
  36.   
  37.     private Object data = null;      
  38.   
  39.     private Lifecycle lifecycle = null;      
  40.   
  41.     private String type = null;      
  42.   
  43.       
  44.   
  45.     public Object getData() {  
  46.   
  47.         return (this.data);  
  48.   
  49.     }  
  50.   
  51.     public Lifecycle getLifecycle() {  
  52.   
  53.         return (this.lifecycle);  
  54.   
  55.     }  
  56.   
  57.     public String getType() {  
  58.   
  59.         return (this.type);  
  60.   
  61.     }  
  62.   
  63. }  

 LifecycleEvent

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

 

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

 

EventObject.

 

LifecycleSupport:

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

Java代碼    收藏代碼
  1. package org.apache.catalina.util;  
  2.   
  3.    
  4.   
  5. import org.apache.catalina.Lifecycle;  
  6.   
  7. import org.apache.catalina.LifecycleEvent;  
  8.   
  9. import org.apache.catalina.LifecycleListener;  
  10.   
  11.    
  12.   
  13. public final class LifecycleSupport {  
  14.   
  15.     public LifecycleSupport(Lifecycle lifecycle) {  
  16.   
  17.         super();  
  18.   
  19.         this.lifecycle = lifecycle;  
  20.   
  21.    
  22.   
  23.     }  
  24.   
  25.    
  26.   
  27.      
  28.   
  29.     private Lifecycle lifecycle = null;    
  30.   
  31.     private LifecycleListener listeners[] = new LifecycleListener[0];  
  32.   
  33.     //添加一個觀察者  
  34.   
  35.     public void addLifecycleListener(LifecycleListener listener) {  
  36.   
  37.       synchronized (listeners) {  
  38.   
  39.           LifecycleListener results[] =  
  40.   
  41.             new LifecycleListener[listeners.length + 1];  
  42.   
  43.           for (int i = 0; i < listeners.length; i++)  
  44.   
  45.               results[i] = listeners[i];  
  46.   
  47.           results[listeners.length] = listener;  
  48.   
  49.           listeners = results;  
  50.   
  51.       }  
  52.   
  53.    
  54.   
  55.     }  
  56.   
  57.     //找出所注冊的觀察者  
  58.   
  59.     public LifecycleListener[] findLifecycleListeners() {  
  60.   
  61.         return listeners;  
  62.   
  63.    
  64.   
  65.     }  
  66.   
  67.     //通知觀察者  
  68.   
  69.     public void fireLifecycleEvent(String type, Object data) {  
  70.   
  71.    
  72.   
  73.         LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);  
  74.   
  75.         LifecycleListener interested[] = null;  
  76.   
  77.         synchronized (listeners) {  
  78.   
  79.             interested = (LifecycleListener[]) listeners.clone();  
  80.   
  81.         }  
  82.   
  83.         for (int i = 0; i < interested.length; i++)  
  84.   
  85.             interested[i].lifecycleEvent(event);  
  86.   
  87.    
  88.   
  89.     }  
  90.   
  91.     //刪除一個觀察者  
  92.   
  93.     public void removeLifecycleListener(LifecycleListener listener) {  
  94.   
  95.    
  96.   
  97.         synchronized (listeners) {  
  98.   
  99.             int n = -1;  
  100.   
  101.             for (int i = 0; i < listeners.length; i++) {  
  102.   
  103.                 if (listeners[i] == listener) {  
  104.   
  105.                     n = i;  
  106.   
  107.                     break;  
  108.   
  109.                 }  
  110.   
  111.             }  
  112.   
  113.             if (n < 0)  
  114.   
  115.                 return;  
  116.   
  117.             LifecycleListener results[] =  
  118.   
  119.               new LifecycleListener[listeners.length - 1];  
  120.   
  121.             int j = 0;  
  122.   
  123.             for (int i = 0; i < listeners.length; i++) {  
  124.   
  125.                 if (i != n)  
  126.   
  127.                     results[j++] = listeners[i];  
  128.   
  129.             }  
  130.   
  131.             listeners = results;  
  132.   
  133.         }  
  134.   
  135.    
  136.   
  137.     }  
  138.   
  139. }  

 

 

 

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

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

 

Java代碼    收藏代碼
  1. protected LifecycleSupport lifecycle = new LifecycleSupport(this);  
  2.   
  3.     public void addLifecycleListener(LifecycleListener listener) {  
  4.   
  5.         lifecycle.addLifecycleListener(listener);  
  6.   
  7.     }  
  8.   
  9.    
  10.   
  11.     public LifecycleListener[] findLifecycleListeners() {  
  12.   
  13.         return lifecycle.findLifecycleListeners();  
  14.   
  15. }  
  16.   
  17.    
  18.   
  19.     public void removeLifecycleListener(LifecycleListener listener) {  
  20.   
  21.         lifecycle.removeLifecycleListener(listener);  
  22.   
  23. }  

 

 

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 Java9.1分,Effective Java9.2分。Tomcat的源代碼非常值得研究,里面用了很多的設計模式,如本文講的觀察者模式,還是上一篇所講的單例模式,以及門面模式,責任鏈模式等等。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM