2025-02-10

在前面的大部分文章都是講連接器和容器的,以後的內容會偏向寫一些Tomcat的其他組件以及一些細節的東西。
   Tomcat有很多組件,要一個一個啟動組件難免有點麻煩。由於Tomcat的包含關系是Catalina->Server->Service->容器/連接器/日志器等,於是可通過父組件負責啟動/關閉它的子組件,這樣隻要啟動Catalina,其他的都自動啟動瞭。這種單一啟動和關閉的機制是通過實現Lifecycle接口來實現的。下面是Lifecycle接口的定義:
[java] 
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 LifecycleListener[] findLifecycleListeners(); 
    public void removeLifecycleListener(LifecycleListener listener); 
    public void start() throws LifecycleException;//組件啟動方法 
    public void stop() throws LifecycleException; 

當組件實現瞭Lifecycle接口,父組件啟動的時候,即調用start方法時,隻要在父組件的start方法中也調用子組件的start方法即可(隻有實現統一的接口Lifecycle才能實現統一調用,如以下調用方式:(Lifecycle)子組件.start()),下面一步一步來看源代碼,首先在Catalina啟動start,部分代碼如下:
[java]
// Start the new server 
        if (server instanceof Lifecycle) { 
            try { 
                server.initialize(); 
                ((Lifecycle) server).start();//啟動server 
                try { 
                    // Register shutdown hook 
                    Runtime.getRuntime().addShutdownHook(shutdownHook); 
                } catch (Throwable t) { 
                    // This will fail on JDK 1.2. Ignoring, as Tomcat can run 
                    // fine without the shutdown hook. 
                } 
                // Wait for the server to be told to shut down 
                server.await(); 
            } catch (LifecycleException e) { 
                System.out.println("Catalina.start: " + e); 
                e.printStackTrace(System.out); 
                if (e.getThrowable() != null) { 
                    System.out.println("—– Root Cause —–"); 
                    e.getThrowable().printStackTrace(System.out); 
                } 
            } 
        } 
關鍵看((Lifecycle) server).start();這樣便在啟動Catalina的時候啟動瞭Server,再看StandardServer的start方法:
[java]
public void start() throws LifecycleException { 
 
    // Validate and update our current component state 
    if (started) 
        throw new LifecycleException 
            (sm.getString("standardServer.start.started")); 
    // Notify our interested LifecycleListeners 
    //發送這個事件 
    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);//發送生命周期事件。 
 
    lifecycle.fireLifecycleEvent(START_EVENT, null); 
    started = true; 
 
    // Start our defined Services 
    synchronized (services) {   //由這裡也可以看出一個server可以有多個services 
        for (int i = 0; i < services.length; i++) { 
            if (services[i] instanceof Lifecycle) 
                ((Lifecycle) services[i]).start(); 
        } 
    } 
 
    // Notify our interested LifecycleListeners 
    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); 
 

主要做瞭兩件事,1:發送生命周期事件給監聽者;2:啟動子組件services(至於server怎麼關聯上services請看前面的幾篇文章,以後都不再題怎麼關聯上的瞭)。
這裡先岔開一下,說一下監聽器,lifecycle是一個工具類LifecycleSupport的實例,每一個組件都有這樣一個工具類,這個工具類的作用就是幫助管理該組件上的監聽器,包括添加監聽器和群發事件給監聽器,看LifecycleSupport類的一些關鍵代碼:
[java] 
public final class LifecycleSupport { 
public LifecycleSupport(Lifecycle lifecycle) { 
 
        super(); 
        this.lifecycle = lifecycle; 
 
    } 
  private LifecycleListener listeners[] = new LifecycleListener[0]; 
 public void addLifecycleListener(LifecycleListener listener) { //向listeners添加監聽器 
 
      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 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);//發送組件生命周期事件。 
 
    } 

先看構造方法,傳入一個lifecycle,因為每個組件都實現瞭lifecycle,所以這裡傳入的實際上是一個組件,即每個組件都有一個LifecycleSupport與之關聯,當要在組件中添加一個監聽器的時候,實際上是添加進工具類LifecycleSupport的一個監聽器數組listeners中,當要發送一個組件生命周期的事件時,工具類就會遍歷監聽器數組,然後再一個一個的發送事件。這裡需要先實現我們自己的監聽器類並且添加進我們需要監聽的組件當中。實現監聽器類隻要實現LifecycleListener接口就行,這個接口隻有一個方法:
[java]
public interface LifecycleListener { 
    public void lifecycleEvent(LifecycleEvent event); 

我們需要做的就是實現LifecycleListener接口來擁有自己的監聽器,在lifecycleEvent方法裡寫自己監聽到事件後該做的事情,然後添加進要監聽的組件就行,比如當我們要看StandardServer是否啟動瞭,在上面StandardServer的start方法有一句這樣的代碼:lifecycle.fireLifecycleEvent(START_EVENT, null);即發送StandardServer啟動的事件給跟它關聯的監聽器。接下來回到一開始,當server啟動後,接著啟動它的子組件service,即調用StandardService的start方法,這個方法跟StandardServer的start方法差不多,隻是啟動瞭連接器和容器,連接器的start方法在前面的文章已經講過瞭,主要是啟動瞭n個處理器HttpProcessor組件。頂級容器是StandardEngine,它的start方法僅僅調用瞭父類ContainerBase的start方法,下面看ContainerBase的start方法:
[java] 
public synchronized void start() throws LifecycleException { 
 
     // Validate and update our current component state 
     if (started) 
         throw new LifecycleException 
             (sm.getString("containerBase.alreadyStarted", logName())); 
 
     // Notify our interested LifecycleListeners 
     lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); 
 
     addDefaultMapper(this.mapperClass); 
     started = true;   www.aiwalls.com
 
     // Start our subordinate components, if any 
     if ((loader != null) && (loader instanceof Lifecycle)) //啟動所有其他的組件 
         ((Lifecycle) loader).start(); 
     if ((logger != null) && (logger instanceof Lifecycle)) 
         ((Lifecycle) logger).start(); 
     if ((manager != null) && (manager instanceof Lifecycle)) 
         ((Lifecycle) manager).start(); 
     if ((cluster != null) && (cluster instanceof Lifecycle)) 
         ((Lifecycle) cluster).start(); 
     if ((realm != null) && (realm instanceof Lifecycle)) 
         ((Lifecycle) realm).start(); 
     if ((resources != null) && (resources instanceof Lifecycle)) 
         ((Lifecycle) resources).start(); 
 
     // Start our Mappers, if any 
     Mapper mappers[] = findMappers(); 
     for (int i = 0; i < mappers.length; i++) { 
         if (mappers[i] instanceof Lifecycle) 
             ((Lifecycle) mappers[i]).start(); 
     } 
 
     // Start our child containers, if any 
     Container children[] = findChildren(); 
     for (int i = 0; i < children.length; i++) { 
         if (children[i] instanceof Lifecycle) 
             ((Lifecycle) children[i]).start(); 
     } 
 
     // Start the Valves in our pipeline (including the basic), if any 
     if (pipeline instanceof Lifecycle) 
         ((Lifecycle) pipeline).start(); 
 
     // Notify our interested LifecycleListeners 
     lifecycle.fireLifecycleEvent(START_EVENT, null); 
 
     // Notify our interested LifecycleListeners 
     lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); 
 
 } 

這裡代碼比較豐富,由它啟動瞭Tomcat其他所有的組件,包括加載器,映射器,日志記錄器,管道等等,由這裡也可以看出,他們都實現瞭Lifecycle接口。統一關閉跟統一啟動的邏輯差不多,這裡就不再說瞭。至此,我們對Tomcat怎麼實現統一啟動/關閉應該有一個比較清晰的認識瞭!

作者:haitao111313

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *