在上篇文章分析 Tomcat 7 的各組件的 init、start 方法時常常會看到有一個 setStateInternal 方法的調用,在查看 LifecycleBase 類及其它各組件的源碼時會在多處看到這個方法的調用,這篇文章就來講說這方法,以及與這個方法相關的 Tomcat 的 Lifecycle 機制和實現原理。java
接上文裏談到了 Tomcat 7 的各組件的父類 LifecycleBase 類,該類實現了接口 org.apache.catalina.Lifecycle,下面是這個接口裏定義的常量和方法: apache
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 接口,那麼就看下這個接口的定義:eclipse
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 )產生時通知當前監聽器的實現類,具體針對該事件如何處理由監聽器實現類本身決定。ide
看下 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 中是如何實現關於這些事件的監聽以及通知的呢?post
在本文開頭提到 的LifecycleBase 類中第 47 行定義了一個實例變量 lifecycle ,正是經過該變量來註冊組件上定義的各種監聽器的。留心一下 lifecycle 這個實例變量,它並非 org.apache.catalina.Lifecycle 類的實例,而是 org.apache.catalina.util.LifecycleSupport 類的實例。正是這個工具類提供了事件監聽和事件通知的功能。ui
先看下實際代碼中是如何給組件發佈時間通知的,看下前面文章中曾經提到過的 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 關係圖:
事件的發佈使用的是推模式,即每發佈一個事件都會通知主題的全部具體觀察者,由各觀察者再來決定是否須要對該事件進行後續處理。
下面再來看看本文一開頭所說的 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 方法的調用,實際上就是向該組件中已註冊的監聽器發佈一個事件。