【高軟作業4】:Tomcat 觀察者模式解析 之 Lifecycle 五分鐘讀懂UML類圖
一. 預備
- 如果你是Windows使用者,使用Eclipse,並且想自行匯入原始碼進行分析,你可能需要:Eclipse 匯入 Tomcat 原始碼
- 如果你已遺忘 觀察者模式,那麼你可以通過該文章回顧:設計模式(五)觀察者模式
- 如果你已遺忘 UML類圖相關知識,那麼你可以通過文章 (五分鐘讀懂UML類圖 )快速回顧
二. 啟程
1. Tomcat元件生命週期
Tomcat中包含多種元件,每個元件有各自的生命週期,而每個生命週期中又包含多種狀態,這些狀態會根據程式的執行而相互轉換,在這個過程中,某些元件會隨之做出相應的反饋。
下面是Tomcat元件生命週期狀態轉換圖,來自tomcat原始碼Lifecycle.java的註釋:
2. Lifecycle中的觀察者模式
生命週期的狀態改變,某些元件會隨之做出相應的反饋,該運作模式符合觀察者模式的應用範圍。下面是Tomcat中關於Lifecycle的觀察者模式類圖:
(為了描述方便,我對類的成員進行簡化,只給出文章講解會用到的成員;如果圖片看不清,滑鼠右鍵->檢視影象,Firefox是這樣的)
3. 實現原理
我們通過 init() 方法的執行過程來看 Lifecycle 觀察者模式的實現原理。
- Lifecycle subject; // 生命週期(主題)
- LifecycleListener observer; // 觀察者
- subject.addLifecycleListener(observer); // 主題將觀察者新增到觀察者列表(觀察者註冊)
- subject.init(); // 生命週期狀態 NEW ----> INITIALIZED
在 生命週期狀態 由 NEW 轉到 INITIALIZED 的過程中,subject 會以 LifecycleEvent 為媒介來通知 observer,說:“我的狀態已經改變,你可以執行相關的操作了”,以 LifecycleBase
1 @Override 2 public final synchronized void init() throws LifecycleException { 3 if (!state.equals(LifecycleState.NEW)) { 4 invalidTransition(Lifecycle.BEFORE_INIT_EVENT); 5 } 6 7 try { 8 setStateInternal(LifecycleState.INITIALIZING, null, false); 9 initInternal(); 10 setStateInternal(LifecycleState.INITIALIZED, null, false); 11 } catch (Throwable t) { 12 handleSubClassException(t, "lifecycleBase.initFail", toString()); 13 } 14 }View Code
- subject.init()
- setStateInternal(LifecycleState.INITIALIZING, null, false); // 設定當前狀態為 INITIALIZING,並通知 observer,說:“我現在的狀態變成了 INITIALIZING,你可以執行相應操作了”
- initInternal(); // 內部初始化(模版方法)
- setStateInternal(LifecycleState.INITIALIZED, null, false); // 設定當前狀態為 INITIALIZED,並通知 observer,說:“我現在狀態改為 INITIALIZED,你可以執行相應操作了”
來看 LifecycleBase 中的 setStateInternal(LifecycleState state, Object data, boolean check) 內部是如何實現的吧(主要看最後面幾行),原始碼如下:
1 private synchronized void setStateInternal(LifecycleState state, Object data, boolean check) 2 throws LifecycleException { 3 if (log.isDebugEnabled()) { 4 log.debug(sm.getString("lifecycleBase.setState", this, state)); 5 } 6 7 if (check) { 8 // Must have been triggered by one of the abstract methods (assume 9 // code in this class is correct) 10 // null is never a valid state 11 if (state == null) { 12 invalidTransition("null"); 13 // Unreachable code - here to stop eclipse complaining about 14 // a possible NPE further down the method 15 return; 16 } 17 18 // Any method can transition to failed 19 // startInternal() permits STARTING_PREP to STARTING 20 // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to 21 // STOPPING 22 if (!(state == LifecycleState.FAILED || 23 (this.state == LifecycleState.STARTING_PREP && 24 state == LifecycleState.STARTING) || 25 (this.state == LifecycleState.STOPPING_PREP && 26 state == LifecycleState.STOPPING) || 27 (this.state == LifecycleState.FAILED && 28 state == LifecycleState.STOPPING))) { 29 // No other transition permitted 30 invalidTransition(state.name()); 31 } 32 } 33 34 this.state = state; 35 String lifecycleEvent = state.getLifecycleEvent(); 36 if (lifecycleEvent != null) { 37 fireLifecycleEvent(lifecycleEvent, data); 38 } 39 }View Code
- "你可以執行相應操作了",並不是在 observer 端執行操作,而是 subject 通過 observer 提供的介面 lifecycleEvent(LifecycleEvent event) 來執行操作
- subject 通過呼叫其內部方法 fireLifecycleEvent(String type, Object data) (會被setStateInternal(...)方法呼叫)使其觀察者的方法 lifecycleEvent(LifecycleEvent event) 得到執行,從而達到訊息專遞的目的
以下是 LifecycleBase 中的 fireLifecycleEvent(String type, Object data) 的內部實現,以及 UserConfig 中的 lifecycleEvent(LifecycleEvent event) 內部實現,原始碼如下:
1 // LifecycleBase.java 2 /** 3 * Allow sub classes to fire {@link Lifecycle} events. 4 * 5 * @param type Event type 6 * @param data Data associated with event. 7 */ 8 protected void fireLifecycleEvent(String type, Object data) { 9 LifecycleEvent event = new LifecycleEvent(this, type, data); 10 for (LifecycleListener listener : lifecycleListeners) { 11 listener.lifecycleEvent(event); 12 } 13 } 14 15 // UserConfig.java 16 /** 17 * Process the START event for an associated Host. 18 * 19 * @param event The lifecycle event that has occurred 20 */ 21 @Override 22 public void lifecycleEvent(LifecycleEvent event) { 23 24 // Identify the host we are associated with 25 try { 26 host = (Host) event.getLifecycle(); 27 } catch (ClassCastException e) { 28 log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e); 29 return; 30 } 31 32 // Process the event that has occurred 33 if (event.getType().equals(Lifecycle.START_EVENT)) 34 start(); 35 else if (event.getType().equals(Lifecycle.STOP_EVENT)) 36 stop(); 37 }View Code
- 至此,流程:“subject 註冊 observer --> subject 狀態改變 --> observer 做出相應的反饋” 結束
三. 結束
觀察者模式給 Tomcat 生命週期管理帶來的好處:
- 解除 生命週期 與 依賴生命週期元件 的耦合,讓雙方都依賴於各自的抽象,使得各自內部實現的變化不會影響到另一方
- 通過介面呼叫,增加了程式設計的靈活性和可擴充套件性,雙方都可根據需要來擴充套件自己的子類,只要該子類按要求實現了相關介面方法就行
- 支援一對多的 訊息專遞 / 事件響應,即:某個事物的狀態改變,會使依賴該事物的其他多個事物 收到訊息 / 做出反饋
本文所涉及的Java原始碼地址:
- Apache Tomcat 專案地址:https://github.com/apache/tomcat
- Lifecycle.java:https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/Lifecycle.java
- LifecycleBase.java:https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/util/LifecycleBase.java
- LifecycleListener.java:https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/LifecycleListener.java
- UserConfig.java:https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/startup/UserConfig.java
- LifecycleState.java:https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/LifecycleState.java
- LifecycleEvent.java:https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/LifecycleEvent.java
轉載請說明出處!have a good time :D