Tomcat 的設計模式分析

Tomcat 中運用的許多經典設計模式,如模版模式、工廠模式和單例模式等。通過學習它們的實踐運用能給我們以後的軟件設計起到一定的借鑑作用。
1. 門面設計模式
門面設計模式在 Tomcat 中有多處使用,在 Request 和Response 對象封裝中、 Standard Wrapper 到 ServletConfig 封裝中、ApplicationContext 到 ServletContext 封裝中等都用到了這種設計模式。
1.1. 門面設計模式的原理

這麼多場合都用到了這種設計模式,那這種設計模式究竟能有什麼作用呢?顧名思義,就是將一個東西封裝成一個門面好與人家更容易進行交流,就像一個國家的外交部一樣。

這種設計模式主要用在一個大的系統中有多個子系統組成時,這多個子系統肯定要涉及到相互通信,但是每個子系統又不能將自己的內部數據過多的暴露給其它系統,不然就沒有必要劃分子系統了。每個子系統都會設計一個門面,把別的系統感興趣的數據封裝起來,通過這個門面來進行訪問。這就是門面設計模式存在的意義。

門面設計模式示意圖如下:


Client 只能訪問到 Façade 中提供的數據是門面設計模式的關鍵,至於 Client 如何訪問 Façade 和 Subsystem 如何提供 Façade 門面設計模式並沒有規定死。
1.2. Tomcat 的門面設計模式示例
Tomcat 中門面設計模式使用的很多,因爲 Tomcat 中有很多不同組件,每個組件要相互交互數據,用門面模式隔離數據是個很好的方法。
下面是 Request 上使用的門面設計模式:


從圖中可以看出 HttpRequestFacade 類封裝了 HttpRequest 接口能夠提供數據,通過 HttpRequestFacade 訪問到的數據都被代理到HttpRequest 中,通常被封裝的對象都被設爲 Private 或者Protected 訪問修飾,以防止在 Façade 中被直接訪問。
2. 觀察者設計模式
這種設計模式也是常用的設計方法通常也叫發佈 - 訂閱模式,也就是事件監聽機制,通常在某個事件發生的前後會觸發一些操作。
2.1. 觀察者模式的原理
觀察者模式原理也很簡單,就是你在做事的時候旁邊總有一個人在盯着你,當你做的事情是它感興趣的時候,它就會跟着做另外一些事情。但是盯着你的人必須要到你那去登記,不然你無法通知它。觀察者模式通常包含下面這幾個角色:
 Subject 就是抽象主題:它負責管理所有觀察者的引用,同時定義主要的事件操作。
 ConcreteSubject 具體主題:它實現了抽象主題的所有定義的接口,當自己發生變化時,會通知所有觀察者。
 Observer 觀察者:監聽主題發生變化相應的操作接口。
2.2. Tomcat 的觀察者模式示例
Tomcat 中觀察者模式也有多處使用,前面講的控制組件生命週期的 Lifecycle 就是這種模式的體現,還有對 Servlet 實例的創建、Session 的管理、 Container 等都是同樣的原理。下面主要看一下Lifecycle 的具體實現。
Lifecycle 的觀察者模式結構圖:


上面的結構圖中, LifecycleListener 代表的是抽象觀察者,它定義一個 lifecycleEvent 方法,這個方法就是當主題變化時要執行的方法。 ServerLifecycleListener 代表的是具體的觀察者,它實現了LifecycleListener 接口的方法,就是這個具體的觀察者具體的實現方式。 Lifecycle 接口代表的是抽象主題,它定義了管理觀察者的方法和它要所做的其它方法。而 StandardServer 代表的是具體主題,它實現了抽象主題的所有方法。這裏 Tomcat 對觀察者做了擴展,增加了另外兩個類: LifecycleSupport、LifecycleEvent,它們作爲輔助類擴展了觀察者的功能。 LifecycleEvent 使得可以定義事件類別,不同的事件可區別處理,更加靈活。 LifecycleSupport 類代理了主題對多觀察者的管理,將這個管理抽出來統一實現,以後如果修改只要修改 LifecycleSupport 類就可以了,不需要去修改所有具體主題,因爲所有具體主題的對觀察者的操作都被代理給 LifecycleSupport 類了。這可以認爲是觀察者模式的改進版。
LifecycleSupport 調用觀察者的方法代碼如下:
清單 1. LifecycleSupport 中的 fireLifecycleEvent 方法
public void fireLifecycleEvent(String type, Object data) {
<span style="white-space:pre">	</span>LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
<span style="white-space:pre">	</span>LifecycleListener interested[] = null;
<span style="white-space:pre">	</span>terested = (LifecycleListener[]) listeners.clone();
}
for (int i = 0; i < interested.length; i++)
<span style="white-space:pre">	</span>interested[i].lifecycleEvent(event);
}

主題是怎麼通知觀察者呢?看下面代碼:
清單 2. 容器中的 start 方法
public void start() throws LifecycleException {
<span style="white-space:pre">	</span>lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
<span style="white-space:pre">	</span>lifecycle.fireLifecycleEvent(START_EVENT, null);
<span style="white-space:pre">	</span>started = true;
<span style="white-space:pre">	</span>synchronized (services) {
<span style="white-space:pre">	</span>for (int i = 0; i < services.length; i++) {
<span style="white-space:pre">		</span>if (services[i] instanceof Lifecycle)
<span style="white-space:pre">			</span>((Lifecycle) services[i]).start();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}

3. 命令設計模式
前面把 Tomcat 中兩個核心組件 Connector 和 Container,比作一對夫妻。男的將接受過來的請求以命令的方式交給女主人。對應到Connector 和 Container, Connector 也是通過命令模式調用Container 的。
3.1. 命令模式的原理
命令模式主要作用就是封裝命令,把發出命令的責任和執行命令的責任分開。也是一種功能的分工。不同的模塊可以對同一個命令做出不同解釋。
下面是命令模式通常包含下面幾個角色:
 Client:創建一個命令,並決定接受者
 Command 命令:命令接口定義一個抽象方法
 ConcreteCommand:具體命令,負責調用接受者的相應操作
 Invoker 請求者:負責調用命令對象執行請求
 Receiver 接受者:負責具體實施和執行一次請求
3.2. Tomcat 中的命令模式的示例
Tomcat 中命令模式在 Connector 和 Container 組件之間有體現, Tomcat 作爲一個應用服務器,無疑會接受到很多請求,如何分配和執行這些請求是必須的功能。

下面看一下 Tomcat 是如何實現命令模式的,下面是 Tomcat 命令模式的結構圖:


Connector 作爲抽象請求者, HttpConnector 作爲具體請求者。HttpProcessor 作爲命令。 Container 作爲命令的抽象接受者,ContainerBase 作爲具體的接受者。客戶端就是應用服務器 Server組件了。 Server 首先創建命令請求者 HttpConnector 對象,然後創建命令 HttpProcessor 命令對象。再把命令對象交給命令接受者ContainerBase 容器來處理命令。命令的最終是被 Tomcat 的Container 執行的。命令可以以隊列的方式進來, Container 也可以以不同的方式來處理請求,如 HTTP1.0 協議和 HTTP1.1 的處理方
式就會不同。
4. 責任鏈模式
Tomcat 中一個最容易發現的設計模式就是責任鏈模式,這個設計模式也是 Tomcat 中 Container 設計的基礎,整個容器的就是通過一個鏈連接在一起,這個鏈一直將請求正確的傳遞給最終處理請求的那個 Servlet。
4.1. 責任鏈模式的原理
責任鏈模式,就是很多對象有每個對象對其下家的引用而連接起來形成一條鏈,請求在這條鏈上傳遞,直到鏈上的某個對象處理此請求,或者每個對象都可以處理請求,並傳給下一家,直到最終鏈上每個對象都處理完。這樣可以不影響客戶端而能夠在鏈上增加任意的處理節點。
通常責任鏈模式包含下面幾個角色:
 Handler(抽象處理者):定義一個處理請求的接口
 ConcreteHandler(具體處理者):處理請求的具體類,或者傳給下家
4.2. Tomcat 中責任鏈模式示例
在 tomcat 中這種設計模式幾乎被完整的使用, tomcat 的容器設置就是責任鏈模式,從 Engine 到 Host 再到 Context 一直到Wrapper 都是通過一個鏈傳遞請求。

Tomcat 中責任鏈模式的類結構圖如下:



上圖基本描述了四個子容器使用責任鏈模式的類結構圖,對應的責任鏈模式的角色, Container 扮演抽象處理者角色,具體處理者由StandardEngine 等子容器扮演。與標準的責任鏈不同的是,這裏引了 Pipeline 和 Valve 接口。他們有什麼作用呢?
實際上 Pipeline 和 Valve 是擴展了這個鏈的功能,使得在鏈往下傳遞過程中,能夠接受外界的干預。 Pipeline 就是連接每個子容器的管子,裏面傳遞的 Request 和Response 對象好比管子裏流的水,而 Valve 就是這個管子上開的一個個小口子,讓你有機會能夠接觸到裏面的水,做一些額外的事情。
爲了防止水被引出來而不能流到下一個容器中,每一段管子最後總有一個節點保證它一定能流到下一個子容器,所以每個容器都有一個 StandardXXXValve。只要涉及到這種有鏈式是處理流程這是一個非常值得借鑑的模式。
發佈了110 篇原創文章 · 獲贊 13 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章