tomcat lifecyclelistener_大公司程序员带你死磕Tomcat系列(五)——容器
死磕Tomcat系列(5)——容器
回顧
在死磕Tomcat系列(1)——整體架構中我們簡單介紹了容器的概念,并且說了在容器中所有子容器的父接口是Container。在死磕Tomcat系列(2)——EndPoint源碼解析中,我們知道了連接器將請求過來的數據解析成Tomcat需要的ServletRequest對象給容器。那么容器又是如何將這個對象準確的分到到對應的請求上去的呢?
容器的整體設計
Container是容器的父接口,所有子容器都需要實現此接口,我們首先看一下Container接口的設計。
public interface Container extends Lifecycle {
public void setName(String name);
public Container getParent();
public void setParent(Container container);
public void addChild(Container child);
public void removeChild(Container child);
public Container findChild(String name);
}
Tomcat是如何管理這些容器的呢?我們可以通過接口的設計可以了解到是通過設置父子關系,形成一個樹形的結構(一父多子)、鏈式結構(一父一子)來管理的。一想到樹形的結構我們應該就立馬能夠聯想到設計模式中的組合模式,而鏈式結構我們應該能夠想到設計模式中的責任鏈設計模式。無論這兩種的哪一種我們都知道這種關系是上下層級的關系。用圖來表示就是如下。
既然是父子的結構,那么連接器是如何將轉換好的ServletRequest給到容器的呢?我們可以看CoyoteAdapter中的service方法。因為在連接器中最后一環是將解析過的Request給到Adapter運用適配器設計模式解析為ServletRequest對象。在service方法中我們看到有這么一句。
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
而其中的getContainer方法,返回的是Engine對象
public Engine getContainer();這里看到了Pipeline,Pipeline應該大家有所熟悉,是管道的概念,那么管道里面裝的是什么呢?我們看其定義的方法
public interface Pipeline extends Contained {
public void addValve(Valve valve);
public Valve getBasic();
public void setBasic(Valve valve);
public Valve getFirst();
}
可以看到Pipeline管道里面裝的是Valve,那么Valve是如何組織起來的呢?我們也可以看它的代碼定義
public interface Valve { public Valve getNext(); public void setNext(Valve valve); public void invoke(Request request, Response response)}可以知道每個Valve都是一個處理點,它的invoke就是相對應的處理邏輯。可以看到有setNext的方法,因此我們大概能夠猜到是通過鏈表將Valve組織起來的。然后將此Valve裝入Pipeline中。因此每個容器都有一個Pipeline,里面裝入系統定義或者自定義的一些攔截節點來做一些相應的處理。因此只要獲得了容器中Pipeline管道中的第一個Valve對象,那么后面一系列鏈條都會執行到。
但是不同容器之間Pipeline之間是如何進行觸發的呢?即例如Engine的Pipeline處理完了最后一個Valve,那么如何調用Host的PipeLine管道中的Valve呢?我們可以看到每個Pipeline中還有一個方法。setBasic這個方法設置的就是Valve鏈條的末端節點是什么,它負責調用底層容器的Pipeline第一個Valve節點。用圖表示就是這樣的。
Engine容器
Engine容器比較簡單,只是定義了一些基本的關聯關系。它的實現類是StandardEngine。
@Override
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
@Override
public void setParent(Container container) {
throw new IllegalArgumentException
(sm.getString("standardEngine.notParent"));
}
需要注意Engine容器是沒有父容器的。如果添加是會報錯。添加子容器也只是能添加Host容器。
Host 容器
Host容器是Engine的子容器,一個Host在Engine中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應用,它負責安裝和展開這個應用,并且標識這個應用以便能夠區分它們。它的子容器通常是Context容器。我們可以看配置文件中也能夠看出Host文件的作用。
那么Host容器在啟動時具體干了什么呢?我們看它的startInternal方法看不出來什么,只是啟動了相應的Valve,是因為在Tomcat的設計中引入了生命周期的概念,即每個模塊都有自己相應的生命周期,模塊的生命周期定義有NEW、INITIALIZING、INITIALIZED、SSTARTING_PREP、STARTING、STARTED,每個模塊狀態的變化都會引發一系列的動作,那么這些動作的執行是直接寫在startInternal中嗎?這樣會違反開閉原則,那么如何解決這個問題呢?開閉原則說的是為了擴展性系統的功能,你不能修改系統中現有的類,但是你可以定義新的類。
于是每個模塊狀態的變化相當于一個事件的發生,而事件是有相應的監聽器的。在監聽器中實現具體的邏輯,監聽器也可以方便的增加和刪除。這就是典型的觀察者模式。
那么Host容器在啟動的時候需要掃描webapps目錄下面的所有Web應用,創建相應的Context容器。那么Host的監聽器就是HostConfig,它實現了LifecycleListener接口
public interface LifecycleListener { public void lifecycleEvent(LifecycleEvent event);}接口中只定義了一個方法,即監聽到相應事件的處理邏輯。可以看到在setState方法中調用了監聽器的觸發。
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
所以容器中各組件的具體處理邏輯是在監聽器中實現的。
Context 容器
一個Context對應一個Web應用
Context代表的是Servlet的Context,它具備了Servlet的運行的基本環境。Context最重要的功能就是管理它里面的Servlet實例,Servlet實例在Context中是以Wrapper出現的。Context準備運行環境是在ContextConfig中lifecycleEvent方法準備的。
@Overridepublic void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with try { context = (Context) event.getLifecycle(); } catch (ClassCastException e) { log.error(sm.getString("contextConfig.cce總結
以上是生活随笔為你收集整理的tomcat lifecyclelistener_大公司程序员带你死磕Tomcat系列(五)——容器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 漂亮界面demo_在这个什
- 下一篇: 启继承父位在什么时候_为什么少儿口才现在