日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

首先我們通過一張簡化的類圖來回顧一下,從圖上你可以看到各種組件的層次關系,圖中的虛線表示一個請求在 Tomcat 中流轉的過程。

趣操作,Tomcat如何實現一鍵式啟停?

 

上面這張圖描述了組件之間的靜態關系,如果想讓一個系統能夠對外提供服務,我們需要創建、組裝并啟動這些組件;在服務停止的時候,我們還需要釋放資源,銷毀這些組件,因此這是一個動態的過程。也就是說,Tomcat 需要動態地管理這些組件的生命周期。

在我們實際的工作中,如果你需要設計一個比較大的系統或者框架時,你同樣也需要考慮這幾個問題:如何統一管理組件的創建、初始化、啟動、停止和銷毀?如何做到代碼邏輯清晰?如何方便地添加或者刪除組件?如何做到組件啟動和停止不遺漏、不重復?

今天我們就來解決上面的問題,在這之前,先來看看組件之間的關系。如果你仔細分析過這些組件,可以發現它們具有兩層關系。

  • 第一層關系是組件有大有小,大組件管理小組件,比如 Server 管理 Service,Service 又管理連接器和容器。
  • 第二層關系是組件有外有內,外層組件控制內層組件,比如連接器是外層組件,負責對外交流,外層組件調用內層組件完成業務功能。也就是說,請求的處理過程是由外層組件來驅動的。

這兩層關系決定了系統在創建組件時應該遵循一定的順序。

  • 第一個原則是先創建子組件,再創建父組件,子組件需要被“注入”到父組件中。
  • 第二個原則是先創建內層組件,再創建外層組件,內層組建需要被“注入”到外層組件。

因此,最直觀的做法就是將圖上所有的組件按照先小后大、先內后外的順序創建出來,然后組裝在一起。不知道你注意到沒有,這個思路其實很有問題!因為這樣不僅會造成代碼邏輯混亂和組件遺漏,而且也不利于后期的功能擴展。

為了解決這個問題,我們希望找到一種通用的、統一的方法來管理組件的生命周期,就像汽車“一鍵啟動”那樣的效果。

一鍵式啟停:LifeCycle接口

我在前面說到過,設計就是要找到系統的變化點和不變點。這里的不變點就是每個組件都要經歷創建、初始化、啟動這幾個過程,這些狀態以及狀態的轉化是不變的。而變化點是每個具體組件的初始化方法,也就是啟動方法是不一樣的。

因此,我們把不變點抽象出來成為一個接口,這個接口跟生命周期有關,叫作 LifeCycle。LifeCycle 接口里應該定義這么幾個方法:init()、start()、stop() 和 destroy(),每個具體的組件去實現這些方法。

理所當然,在父組件的 init() 方法里需要創建子組件并調用子組件的 init() 方法。同樣,在父組件的 start() 方法里也需要調用子組件的 start() 方法,因此調用者可以無差別的調用各組件的 init() 方法和 start() 方法,這就是組合模式的使用,并且只要調用最頂層組件,也就是 Server 組件的 init() 和 start() 方法,整個 Tomcat 就被啟動起來了。下面是LifeCycle 接口的定義。

趣操作,Tomcat如何實現一鍵式啟停?

 

可擴展性:LifeCycle事件

我們再來考慮另一個問題,那就是系統的可擴展性。因為各個組件 init() 和 start() 方法的具體實現是復雜多變的,比如在 Host 容器的啟動方法里需要掃描 webApps 目錄下的Web 應用,創建相應的 Context 容器,如果將來需要增加新的邏輯,直接修改 start() 方法?這樣會違反開閉原則,那如何解決這個問題呢?開閉原則說的是為了擴展系統的功能,你不能直接修改系統中已有的類,但是你可以定義新的類。

我們注意到,組件的 init() 和 start() 調用是由它的父組件的狀態變化觸發的,上層組件的初始化會觸發子組件的初始化,上層組件的啟動會觸發子組件的啟動,因此我們把組件的生命周期定義成一個個狀態,把狀態的轉變看作是一個事件。而事件是有監聽器的,在監聽器里可以實現一些邏輯,并且監聽器也可以方便的添加和刪除,這就是典型的觀察者模式。

具體來說就是在 LifeCycle 接口里加入兩個方法:添加監聽器和刪除監聽器。除此之外,我們還需要定義一個 Enum 來表示組件有哪些狀態,以及處在什么狀態會觸發什么樣的事件。因此 LifeCycle 接口和 LifeCycleState 就定義成了下面這樣。

趣操作,Tomcat如何實現一鍵式啟停?

 

從圖上你可以看到,組件的生命周期有 NEW、INITIALIZING、INITIALIZED、STARTING_PREP、STARTING、STARTED 等,而一旦組件到達相應的狀態就觸發相應的事件,比如 NEW 狀態表示組件剛剛被實例化;而當 init() 方法被調用時,狀態就變成INITIALIZING 狀態,這個時候,就會觸發 BEFORE_INIT_EVENT 事件,如果有監聽器在監聽這個事件,它的方法就會被調用。

重用性:LifeCycleBase抽象基類

有了接口,我們就要用類去實現接口。一般來說實現類不止一個,不同的類在實現接口時往往會有一些相同的邏輯,如果讓各個子類都去實現一遍,就會有重復代碼。那子類如何重用這部分邏輯呢?其實就是定義一個基類來實現共同的邏輯,然后讓各個子類去繼承它,就達到了重用的目的。

而基類中往往會定義一些抽象方法,所謂的抽象方法就是說基類不會去實現這些方法,而是調用這些方法來實現骨架邏輯。抽象方法是留給各個子類去實現的,并且子類必須實現,否則無法實例化。

比如寶馬和榮威的底盤和骨架其實是一樣的,只是發動機和內飾等配套是不一樣的。底盤和骨架就是基類,寶馬和榮威就是子類。僅僅有底盤和骨架還不是一輛真正意義上的車,只能算是半成品,因此在底盤和骨架上會留出一些安裝接口,比如安裝發動機的接口、安裝座椅的接口,這些就是抽象方法。寶馬或者榮威上安裝的發動機和座椅是不一樣的,也就是具體子類對抽象方法有不同的實現。

回到 LifeCycle 接口,Tomcat 定義一個基類 LifeCycleBase 來實現 LifeCycle 接口,把一些公共的邏輯放到基類中去,比如生命狀態的轉變與維護、生命事件的觸發以及監聽器的添加和刪除等,而子類就負責實現自己的初始化、啟動和停止等方法。為了避免跟基類中的方法同名,我們把具體子類的實現方法改個名字,在后面加上 Internal,叫 initInternal()、startInternal() 等。我們再來看引入了基類 LifeCycleBase 后的類圖:

趣操作,Tomcat如何實現一鍵式啟停?

 

從圖上可以看到,LifeCycleBase 實現了 LifeCycle 接口中所有的方法,還定義了相應的抽象方法交給具體子類去實現,這是典型的模板設計模式。

我們還是看一看代碼,可以幫你加深理解,下面是 LifeCycleBase 的 init() 方法實現。

@Override
public final synchronized void init() throws LifecycleException {
    //1.狀態檢查
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    try {
        //2.觸發INITIALIZING事件的監聽器
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        //3.調用具體子類的初始化方法
        initInternal();
        //4.觸發INITIALIZED事件的監聽器
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
    catch (Throwable t) {
        ...
    }
}

這個方法邏輯比較清楚,主要完成了四步:

第一步,檢查狀態的合法性,比如當前狀態必須是 NEW 然后才能進行初始化。

第二步,觸發 INITIALIZING 事件的監聽器:


 

在這個 setStateInternal 方法里,會調用監聽器的業務方法。

第三步,調用具體子類實現的抽象方法 initInternal() 方法。我在前面提到過,為了實現一鍵式啟動,具體組件在實現 initInternal() 方法時,又會調用它的子組件的 init() 方法。

第四步,子組件初始化后,觸發 INITIALIZED 事件的監聽器,相應監聽器的業務方法就會被調用。

setStateInternal(LifecycleState.INITIALIZED, null, false);

總之,LifeCycleBase 調用了抽象方法來實現骨架邏輯。講到這里, 你可能好奇,LifeCycleBase 負責觸發事件,并調用監聽器的方法,那是什么時候、誰把監聽器注冊進來的呢?

分為兩種情況:

  • Tomcat 自定義了一些監聽器,這些監聽器是父組件在創建子組件的過程中注冊到子組件的。比如 MemoryLeakTrackingListener 監聽器,用來檢測 Context 容器中的內存泄漏,這個監聽器是 Host 容器在創建 Context 容器時注冊到 Context 中的。
  • 我們還可以在 server.xml 中定義自己的監聽器,Tomcat 在啟動時會解析 server.xml,創建監聽器并注冊到容器組件。

生周期管理總體類圖

通過上面的學習,我相信你對 Tomcat 組件的生命周期的管理有了深入的理解,我們再來看一張總體類圖繼續加深印象。

趣操作,Tomcat如何實現一鍵式啟停?

 

這里請你注意,圖中的 StandardServer、StandardService 等是 Server 和 Service 組件的具體實現類,它們都繼承了 LifeCycleBase。

StandardEngine、StandardHost、StandardContext 和 StandardWrapper 是相應容器組件的具體實現類,因為它們都是容器,所以繼承了 ContainerBase 抽象基類,而ContainerBase 實現了 Container 接口,也繼承了 LifeCycleBase 類,它們的生命周期管理接口和功能接口是分開的,這也符合設計中接口分離的原則。

小結

Tomcat 為了實現一鍵式啟停以及優雅的生命周期管理,并考慮到了可擴展性和可重用性,將面向對象思想和設計模式發揮到了極致,分別運用了組合模式、觀察者模式、骨架抽象類和模板方法。

如果你需要維護一堆具有父子關系的實體,可以考慮使用組合模式。觀察者模式聽起來“高大上”,其實就是當一個事件發生后,需要執行一連串更新操作。傳統的實現方式是在事件響應代碼里直接加更新邏輯,當更新邏輯加多了之后,代碼會變得臃

腫,并且這種方式是緊耦合的、侵入式的。而觀察者模式實現了低耦合、非侵入式的通知與更新機制。

而模板方法在抽象基類中經常用到,用來實現通用邏輯。

分享到:
標簽:Tomcat
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定