門面模式在 Tomcat 中有多處使用,在 Request 和 Response 物件封裝中、Standard Wrapper 到 ServletConfig 封裝中、ApplicationContext 到 ServletContext 封裝中等都用到了這種設計模式。
這麼多場合都用到了這種設計模式,那這種設計模式究竟能有什麼作用呢?顧名思義,就是將一個東西封裝成一個門面好與人家更容易進行交流,就像一個國家的外交部一樣。
這種設計模式主要用在一個大的系統中有多個子系統組成時,這多個子系統肯定要涉及到相互通信,但是每個子系統又不能將自己的內部數據過多的暴露給其它系統,不然就沒有必要劃分子系統了。每個子系統都會設計一個門面,把別的系統感興趣的資料封裝起來,透過這個門面來進行存取。這就是門面設計模式存在的意義。
門面設計模式示意圖如下:
#Client 只能存取 Façade 中提供的資料是門面設計模式的關鍵,至於 Client 如何存取 Façade 和 Subsystem 如何提供 Façade 門面設計模式並沒有規定死。
Tomcat 中門面設計模式使用的很多,因為 Tomcat 中有很多不同元件,每個元件要相互交互數據,用門面模式隔離資料是個很好的方法。
以下是 Request 上使用的門面設計模式:
#從圖中可以看出HttpRequestFacade 類別封裝了HttpRequest 介面能夠提供數據,透過HttpRequestFacade 存取的資料都被代理到HttpRequest 中,通常被封裝的物件都被設為Private 或Protected 存取修飾,以防止在在Façade 中被直接訪問。
這種設計模式也是常用的設計方法通常也叫發布 - 訂閱模式,也就是事件監聽機制,通常在某個事件發生的前後會觸發一些操作。
觀察者模式原理也很簡單,就是你在做事的時候旁邊總有一個人在盯著你,當你做的事情是它感興趣的時候,它就會跟著做另外一些事情。但是盯著你的人必須要到你那去登記,不然你無法通知它。觀察者模式通常包含下面這幾個角色:
Tomcat 中觀察者模式也有多處使用,前面講的控制元件生命週期的 Lifecycle 就是這種模式的體現,還有對 Servlet 實例的創建、Session 的管理、Container 等都是同樣的原理。以下主要看一下 Lifecycle 的具體實作。
Lifecycle 的觀察者模式結構圖:
#在上面的結構圖中,LifecycleListener 代表的是抽象觀察者,它定義一個 lifecycleEvent 方法,這個方法就是當主題改變時要執行的方法。 ServerLifecycleListener 代表的是具體的觀察者,它實作了 LifecycleListener 介面的方法,就是這個具體的觀察者俱體的實作方式。 Lifecycle 介面代表的是抽象主題,它定義了管理觀察者的方法和它要所做的其它方法。而 StandardServer 代表的是具體主題,它實作了抽象主題的所有方法。這裡 Tomcat 對觀察者做了擴展,增加了另外兩個類別:LifecycleSupport、LifecycleEvent,它們作為輔助類別擴展了觀察者的功能。 LifecycleEvent 使得可以定義事件類別,不同的事件可區別處理,更有彈性。 LifecycleSupport 類別代理了主題對多觀察者的管理,將這個管理抽出來統一實現,以後如果修改只要修改LifecycleSupport 類別就可以了,不需要去修改所有具體主題,因為所有具體主題的對觀察者的操作都被代理給LifecycleSupport 類別了。這可以認為是觀察者模式的改良版。
LifecycleSupport 呼叫觀察者的方法程式碼如下:
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> fireLifecycleEvent(String type, Object data) { LifecycleEvent event </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> LifecycleEvent(lifecycle, type, data); LifecycleListener interested[] </span>= <span style="color: #0000ff">null</span><span style="color: #000000">; </span><span style="color: #0000ff">synchronized</span><span style="color: #000000"> (listeners) { interested </span>=<span style="color: #000000"> (LifecycleListener[]) listeners.clone(); } </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < interested.length; i++<span style="color: #000000">) interested[i].lifecycleEvent(event); }</span>
主題是怎麼通知觀察者呢?看下面程式碼:
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> start() <span style="color: #0000ff">throws</span><span style="color: #000000"> LifecycleException { lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">); lifecycle.fireLifecycleEvent(START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">); started </span>= <span style="color: #0000ff">true</span><span style="color: #000000">; </span><span style="color: #0000ff">synchronized</span><span style="color: #000000"> (services) { </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < services.length; i++<span style="color: #000000">) { </span><span style="color: #0000ff">if</span> (services[i] <span style="color: #0000ff">instanceof</span><span style="color: #000000"> Lifecycle) ((Lifecycle) services[i]).start(); } } lifecycle.fireLifecycleEvent(AFTER_START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">); }</span>
來看看Tomcat7中這部分的程式碼,上一篇文章說過元件的生命週期是由包含元件的父容器來管理的,Service的啟動過程就由Server管理,Server介面的標準實作類別是StandardServer類,StandardServer中實作了startInernal()方法,就是循環啟動StandServer管理的Service的過程,Tomcat的Service都實作了Lifecycle接口,所以被管理的Service都會被通知到,從而執行start()方法,startIntenal ()方法是這樣的:
<span style="color: #008000">/**</span><span style="color: #008000"> * Start nested components ({</span><span style="color: #808080">@link</span><span style="color: #008000"> Service}s) and implement the requirements * of {</span><span style="color: #808080">@link</span><span style="color: #008000"> org.apache.catalina.util.LifecycleBase#startInternal()}. * * </span><span style="color: #808080">@exception</span><span style="color: #008000"> LifecycleException if this component detects a fatal error * that prevents this component from being used </span><span style="color: #008000">*/</span><span style="color: #000000"> @Override </span><span style="color: #0000ff">protected</span> <span style="color: #0000ff">void</span> startInternal() <span style="color: #0000ff">throws</span><span style="color: #000000"> LifecycleException { fireLifecycleEvent(CONFIGURE_START_EVENT, </span><span style="color: #0000ff">null</span><span style="color: #000000">); setState(LifecycleState.STARTING); globalNamingResources.start(); </span><span style="color: #008000">//</span><span style="color: #008000"> Start our defined Services</span> <span style="color: #0000ff">synchronized</span><span style="color: #000000"> (services) { </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < services.length; i++<span style="color: #000000">) { services[i].start(); } } }</span>
現在所有的Service就會收到通知繼而執行start方法。如果一個Service不允許被使用將會拋出一個LifecycleException異常。
stopIntenal()會通知所有Service執行stop方法,具體處理流程與startIntenal()方法類似。
上面程式碼清單關鍵在fireLifecycleEvent()方法,其執行流程如下:
fireLifecycleEvent(String type, Object data)的方法如下:
<span style="color: #008000">/**</span><span style="color: #008000"> * Notify all lifecycle event listeners that a particular event has * occurred for this Container. The default implementation performs * this notification synchronously using the calling thread. * * </span><span style="color: #808080">@param</span><span style="color: #008000"> type Event type * </span><span style="color: #808080">@param</span><span style="color: #008000"> data Event data </span><span style="color: #008000">*/</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> fireLifecycleEvent(String type, Object data) { LifecycleEvent event </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> LifecycleEvent(lifecycle, type, data); LifecycleListener interested[] </span>=<span style="color: #000000"> listeners; </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < interested.length; i++<span style="color: #000000">) interested[i].lifecycleEvent(event); }</span>
所以,特定事件的通知是由LifecycleListener介面的lifecycleEvent方法完成的,各實作類別可以根據不同的情況實作不同的事件監聽邏輯。
前面把 Tomcat 中兩個核心組件 Connector 和 Container,比喻成一對夫妻。男的將接受過來的請求以命令的方式交給女主人。對應到 Connector 和 Container,Connector 也是透過指令模式呼叫 Container 的。
命令模式主要功能就是封裝命令,把發出命令的責任和執行命令的責任分開。也是一種功能的分工。不同的模組可以對同一個指令做出不同解釋。
下面是命令模式通常包含下面幾個角色:
Tomcat 中命令模式在 Connector 和 Container 元件之間有體現,Tomcat 作為一個應用程式伺服器,無疑會接受到很多請求,如何分配和執行這些請求是必須的功能。
下面來看看 Tomcat 是如何實現指令模式的,以下是 Tomcat 指令模式的結構圖:
#Connector 作為抽象請求者,HttpConnector 作為具體請求者。 HttpProcessor 作為指令。 Container 作為命令的抽象接受者,ContainerBase 作為具體的接受者。客戶端就是應用伺服器 Server 元件了。 Server 先建立指令請求者 HttpConnector 對象,然後建立指令 HttpProcessor 指令對象。再把命令物件交給指令接受者 ContainerBase 容器來處理指令。指令的最終是被 Tomcat 的 Container 執行的。命令可以以佇列的方式進來,Container 也可以以不同的方式來處理請求,如 HTTP1.0 協定和 HTTP1.1 的處理方式就會不同。
Tomcat 中一個最容易發現的設計模式就是責任鏈模式,這個設計模式也是Tomcat 中Container 設計的基礎,整個容器的就是透過一個鏈連接在一起,這個鏈一直將請求正確的傳遞給最終處理請求的那個Servlet。
責任鏈模式,就是許多物件有每個物件對其下家的引用而連接起來形成一條鏈,請求在這條鏈上傳遞,直到鏈上的某個物件處理此請求,或每個物件都可以處理請求,並傳給下一家,直到最終鏈上每個物件都處理完。這樣可以不影響客戶端而能夠在鏈上增加任意的處理節點。
通常責任鏈模式包含以下幾個角色:
在 tomcat 中這種設計模式幾乎被完整的使用,tomcat 的容器設定就是責任鏈模式,從 Engine 到 Host 再到 Context 一直到 Wrapper 都是透過一個鏈傳遞請求。
Tomcat 中責任鏈模式的類別結構圖如下:
#上圖基本上描述了四個子容器使用責任鏈模式的類別結構圖,對應的責任鏈模式的角色,Container 扮演抽象處理者角色,具體處理者由 StandardEngine 等子容器扮演。與標準的責任鏈不同的是,這裡引入了 Pipeline 和 Valve 介面。他們有什麼作用呢?
其實 Pipeline 和 Valve 是擴展了這個鏈的功能,使得在鏈往下傳遞過程中,能夠接受外界的介入。 Pipeline 就是連接每個子容器的管子,裡面傳遞的Request 和Response 物件好比管子裡流的水,而Valve 就是這個管子上開的一個個小口子,讓你有機會能夠接觸到裡面的水,做一些額外的事情。
為了防止水被引出來而不能流到下一個容器中,每一段管子最後總有一個節點保證它一定能流到下一個子容器,所以每個容器都有一個 StandardXXXValve。只要涉及到這種有鍊式是處理流程這是一個非常值得借鏡的模式。
轉至:
以上是【Tomcat】Tomcat相關設計模式分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!