How tomcat works——6 生命週期
概述
Catalina 由多個元件組成,當 Catalina 啟動時,這些元件也需要很好地啟動。當Catalina 停止時,這些元件也必須有機會被清除。例如,當一個容器停止工作時,它必須呼叫所有已載入 servlet 的 destroy()方法,而 session 管理器要把session儲存到二級儲存中。保持元件啟動和停止一致的機制是通過實現org.apache.catalina.Lifecycle 介面來實現的。
一個實現了 Lifecycle 介面的元件會觸發一個或多個下列事件:BEFORE_START_EVENT,START_EVENT,AFTER_START_EVENT,BEFORE_STOP_EVENT,STOP_EVENT和AFTER_STOP_EVENT。當元件被啟動時前3個事件會被觸發,而元件停止時會觸發後邊3個事件。一個事件是通過org.apache.catalina.LifecycleEvent類來表示。另外,如果一個元件可以觸發事件,那麼必須存在相應的監聽器來對觸發的事件作出響應。監聽器是通過org.apache.catalina.LifecycleListener介面來表示。
本章會對 Lifecycle, LifecycleEvent和LifecycleListener 進行討論。另外,還會解釋一個公用類 LifecycleSupport——它給元件提供了一個簡單方式來觸發生命週期事件和處理生命週期監聽器。在本章,我們會建立一個有實現了Lifecycle 介面的類的工程。該程式基於第5章應用程式。
6.1 Lifecycle介面
Catalina 的設計允許一個元件可以包含其它元件。例如一個容器可以包含一系列的元件如載入器、管理器等。父元件負責啟動和停止其子元件。Catalina被設計成所有的元件僅被一個父元件來監護管理,所以啟動 bootstrap類只需啟動一個元件即可。這種單一的啟動停止機制是通過繼承 Lifecycle 來實現。
看 Listing6.1 所示的 Lifecycle 介面:
Listing 6.1: Lifecycle 介面
package org.apache.catalina;
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() 和 stop() 方法。一個元件提供了這些方法的實現,所以它的父元件可以通過這些方法來啟動和停止它們。另外 3 個方法addLifecycleListener(), findLifecycleListeners()和removeLifecycleListener() 是跟監聽器相關。元件中可以具有對在該元件中發生的事件感興趣的監聽器。 當事件發生時,將會通知對該事件感興趣的監聽器。可以由Lifecycle例項觸發的6個事件的名稱在介面中public static final String定義。
6.2 LifecycleEvent類
org.apache.catalina.LifecycleEvent類描繪了一個生命週期事件,如 Listing6.2所示:
Listing 6.2: org.apache.catalinaLifecycleEvent 類
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);
}
}
6.3 LifecycleListener介面
org.apache.catalina.LifecycleListener 介面表示生命週期監聽器,如Listing6.3 所示:
Listing 6.3: org.apache.catalina.LifecycleListener 介面
package org.apache.catalina;
import java.util.EventObject;
public interface LifecycleListener {
public void lifecycleEvent(LifecycleEvent event);
}
在這個介面中只有lifecycleEvent()一個方法。 當監聽器感興趣的事件觸發時,呼叫此方法。
6.4 LifecycleSupport類
一個實現了 Lifecycle 介面的元件並且允許監聽器註冊其“感興趣”的事件,那麼它必須提供跟事件相關的方法程式碼,方法在 Lifecycle 介面中(addLifecycleListener(),findLifecycleListeners() 和 removeLifecycleListener())。這樣該元件就可以將其所有監聽器新增到陣列或 ArrayList 或者其他相似的物件中。Catalina 提供了一個公用類 org.apache.catalina.util.LifecycleSupport 來簡化元件處理監聽器和觸發生命週期事件。LifecycleSupport 如 Listing6.4 所示:
Listing 6.4: 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;
}
}
}
如 Listing 6.4 所示,LifecycleSupport 類儲存所有的生命週期監聽器到一個數組中,該listenens陣列,它初始化時沒有任何成員:
private LifecycleListener listeners[] = new LifecycleListener[0];
當一個監聽器通過 addLifecycleListener()方法被新增時,一個新陣列(長度比舊陣列大 1)會被建立。然後舊陣列中的元素會被拷貝到新陣列中並把新事件新增到陣列中。當一個事件被刪除時,一個新的陣列(長度為舊陣列-1)會被建立並將所有的元素儲存到其中。
fireLifecycleEvent()方法觸發一個生命週期事件。首先,它會克隆整個監聽器陣列,然後它呼叫每個成員的 lifecycleEvent()方法,傳遞被觸發的事件。
一個實現了 Lifecycle 的元件可以使用 LifecycleSupport 類。例如,該程式的SimpleContext 類聲明瞭如下變數:
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
SimpleContext呼叫LifecycleSupport 的addLifecycleListener()方法新增一個生命週期事件:
public void addLifecycleListener(LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
}
要刪除一個生命週期監聽事件,SimpleContext 呼叫 LifecycleSupport 類的removeLifecycleListener()方法:
public void removeLifecycleListener(LifecycleListener listener) {
lifecycle.removeLifecycleListener(listener);
}
要觸發一個事件,SimpleContext 需要呼叫 LifecycleSupport 的fireLifecycleEvent()方法:
lifecycle.fireLifecycleEvent(START_EVENT, null);
6.5 應用Demo
本章應用Demo構建在第5章的程式之上,用來說明 Lifecycle 介面以及與生命週期相關的其它型別的使用。它包括一個上下文(context)和兩個包裝器(wrapper)以及一個載入器(loader)和一個對映(mapper)。該應用程式中的元件實現了 Lifecycle 介面,上下文有一個監聽器。為了使程式簡便,第5章中的2個閥門並沒有使用。該程式的結構如圖 6.1 所示。注意一些介面(Container, Wrapper, Context, Loader, Mapper)一些類(SimpleContextValve, SimpleContextMapper, and SimpleWrapperValve)並沒有出現在該結構圖中。
圖 6.1
注意 SimpleContextLifecycleListener 類表示了 SimpleContext 的監聽器。SimpleContextValve, SimpleContextMapper和 SimpleWrapperValve 類跟第5章的程式相同,這裡不再討論。
6.5.1 ex06.pyrmont.core.SimpleContext
該程式中的 SimpleContext 類跟第5章中的相似,不同的是它實現了 Lifecycle介面。SimpleContext 類使用如下變數來引用一個 LifecycleSupport 例項:
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
它還使用一個名為 started 的變數來標記該 SimpleContext 例項是否已經啟動。SimpleContext 類實現了 Lifecycle 介面中的方法,這些方法如 Listing6.5 所示:
Listing 6.5: Lifecycle 介面方法實現:
public void addLifecycleListener(LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
}
public LifecycleListener[] findLifecycleListeners() {
return null;
}
public void removeLifecycleListener(LifecycleListener listener) {
lifecycle.removeLifecycleListener(listener);
}
public synchronized void start() throws LifecycleException {
if (started)
throw new LifecycleException("SimpleContext has already started");
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
started = true;
try {
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).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);
}catch (Exception e) {
e.printStackTrace();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
public void stop() throws LifecycleException {
if (!started)
throw new LifecycleException("SimpleContext has not been started");
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
try {
// Stop the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) (
((Lifecycle) pipeline).stop();
}
// Stop our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).stop();
}
if ((loader != null) && (loader instanceof Lifecycle)) {
((Lifecycle) loader).stop();
}
}catch (Exception e) {
e.printStackTrace();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}
注意 start() 方法是如何啟動所有子容器以及其相關元件(Loader, Pipeline和Mapper),以及 stop()如何停止這些元件的?使用該機制,可以啟動容器模型中所有的元件,我們只需要啟動最高層的元件即可(在該例子中指SimpleContext 例項)。而停止它們的時候只需簡單停止相同的元件(SimpleContext)即可。
SimpleContext 的 start() 方法首先會檢查是否已預先啟動,如果已經啟動了,則會丟擲 LifecycleException 異常。
if (started)
throw new LifecycleException("SimpleContext has already started");
.
然後它產生了 BEFORE_START_EVENT 事件:
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
其結果就是 SimpleContext 例項中註冊的監聽器都會被喚醒。在該Demo中,一個型別為SimpleContextLifecycleListener的監聽器會註冊它“感興趣”的事件。當我們討論SimpleContextLifecycleListener類時,將會看到此監聽器發生了什麼事。
接下來,start() 方法設定 started 布林變數為true來標記該元件已經啟動:
started = true;
start() 方法接下來會啟動所有元件以及其子容器。現在有2個元件實現了Lifecycle 介面:SimpleLoader 和 SimplePipeline。SimpleContext 有2個包裝器作為其子容器。這些包裝器也實現了 Lifecycle 介面:
try {
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).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();
元件和子容器都被啟動之後,start() 方法產生START_EVENT 和AFTER_START_EVENT這2個事件:
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
....
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
stop()方法首先檢查該例項是否已經啟動。如果沒有啟動則會產生一個LifecycleException 型別異常:
if (!started)
throw new LifecycleException("SimpleContext has not been started");
接下來會觸發 BEFORE_STOP_EVENT 和 STOP_EVENT 事件,並重置 started 變數:
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
接下來 stop() 方法會停止所有相關元件以及 SimpleContext 的子容器:
try {
// Stop the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) (
((Lifecycle) pipeline).stop();
}
// Stop our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).stop();
}
if ((loader != null) && (loader instanceof Lifecycle)) {
((Lifecycle) loader).stop();
}
}catch (Exception e) {
e.printStackTrace();
}
最後,產生 AFTER_STOP_EVENT 事件:
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
6.5.2 ex06.pyrmont.core. SimpleContextLifecycleListener
SimpleContextLifecycleListener 表示 SimpleContext 例項的監聽器。
Listing 6.6: SimpleContextLifecycleListener 類
package ex06.pyrmont.core;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
public class SimpleContextLifecycleListener implements LifecycleListener {
public void lifecycleEvent(LifecycleEvent event) {
Lifecycle lifecycle = event.getLifecycle();
System.out.println("SimpleContextLifecycleListener's event " +
event.getType().toString());
if (Lifecycle.START_EVENT.equals(event.getType())) {
System.out.println("Starting context.");
}
else if (Lifecycle.STOP_EVENT.equals(event.getType())) {
System.out.println("Stopping context.");
}
}
}
SimpleContextLifecycleListener 類中 lifecycleEvent()方法的實現很簡單。只是打印出觸發的型別,如果事件為 START_EVENT 事件,則打印出“Starting context.”,如果事件為 STOP_EVENTT,則打印出“Stopping contex”。
6.5.3 ex06.pyrmont.core. SimpleLoader
SimpleLoader 跟第5章中相似,不同之處在於它實現了 Lifecycle 介面,所實現的方法什麼都沒做只是打印出一些字串到控制檯上。更重要的是,實現了Lifecycle 介面,一個 SimpleLoader 例項就可以通過其相關容器啟動它。
Lifecycle 介面方法實現如 Listing6.7 所示:
Listing 6.7: Lifecycle介面方法在SimpleLoader 實現:
public void addLifecycleListener(LifecycleListener listener) { }
public LifecycleListener[] findLifecycleListeners() {
return null;
}
public void removeLifecycleListener(LifecycleListener listener) { }
public synchronized void start() throws LifecycleException {
System.out.println("Starting SimpleLoader");
}
public void stop() throws LifecycleException { }
6.5.4 ex06.pyrmont.core. SimplePipeline
另外Pipeline介面和SimplePipeline類也實現了Lifecycle介面。從Lifecycle介面繼承來的方法被留空。現在這個類的例項可以由其相關容器來啟動,該類的其它部分跟第5章中的相似。
6.5.5 ex06.pyrmont.core. SimpleWrapper
該類跟 ex05.pyrmont.core.SimpleWrapper 很相似。在該應用中,它實現了Lifecycle 介面,所以它可以由其父容器來啟動。在該程式中除start() 和 stop()方法之外的大多數從 Lifecycle 介面獲得的方法被留空。Listing6.8 展示了這些方法的實現:
Listing 6.8: The methods from the Lifecycle interface
public void addLifecycleListener(LifecycleListener listener) { }
public LifecycleListener[] findLifecycleListeners() {
return null;
}
public void removeLifecycleListener(LifecycleListener listener) {}
public synchronized void start() throws LifecycleException {
System.out.println("Starting Wrapper " + name);
if (started)
throw new LifecycleException("Wrapper already started");
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
started = true;
// Start our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).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);
}
public void stop() throws LifecycleException {
System.out.println("Stopping wrapper " + name);
// Shut down our servlet instance (if it has been initialized)
try {
instance.destroy();
}catch (Throwable t) {
instance = null;
if (!started)
throw new LifecycleException("Wrapper " + name + " not started");
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
// Stop the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).stop();
}
// Stop our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle)) {
((Lifecycle) loader).stop();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}
SimpleWrapper 中的 start() 方法跟 SimpleContext 類中的 start() 方法相似。它啟動所有的元件並觸發 BEFORE_START_EVENT, START_EVENT和AFTER_START_EVENT 事件。
SimpleWrapper的stop()更有趣,它打印出一個簡單的字串,並呼叫servlet例項的 destroy() 方法。
System.out.println("Stopping wrapper " + name);
// Shut down our servlet instance (if it has been initialized)
try {
instance.destroy();
}catch (Throwable t) {}
instance = null;
然後它檢查該包裝器是否被啟動了,如果沒有會丟擲 LifecycleException:
if (!started)
throw new LifecycleException("Wrapper " + name + " not started");
接下來,它觸發 BEFORE_STOP_EVENT 和 STOP_EVENT 事件並重置 started 布林變數:
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
接下來它停止跟其相關的管道元件(pipeline)和載入器(loader)。在該應用程式中,SimpleWrapper例項沒有載入器。
// Stop the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).stop();
}
// Stop our subordinate components, if any
if ((loader != null) && (loader instanceof Lifecycle)) {
((Lifecycle) loader).stop();
}
最後,它觸發了 AFTER_STOP_EVENT 事件:
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
6.5.6 執行Demo
在 windows 下,可以在工作目錄下面如下執行該程式:
java -classpath ./lib/servlet.jar;./ ex06.pyrmont.startup.Bootstrap
在 Linux 下,使用冒號分開兩個庫:
java -classpath ./lib/servlet.jar:./ ex06.pyrmont.startup.Bootstrap
我們可在控制檯看到如下資訊。注意有哪些事件被觸發?
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
SimpleContextLifecycleListener's event before_start
Starting SimpleLoader
Starting Wrapper Modern
Starting Wrapper Primitive
SimpleContextLifecycleListener's event start
Starting context.
SimpleContextLifecycleListener's event after_start
呼叫PrimitiveServlet,可以使用下面的 URL 來請求:
http://localhost:8080/Primitive
呼叫ModernServlet,可以使用下面的 URL 來請求:
http://localhost:8080/Modern
6.6 小結
在本章中,我們學習瞭如何使用Lifecycle介面。 這個介面定義了元件的生命週期,並提供了一種優雅方式傳送事件到其它元件。 此外,Lifecycle介面也實現了可以通過一個單獨的start()/stop()來啟動和停止Catalina中的所有元件。