Tomcat工作原理(五)-tomcat容器
一個tomcat容器需要實現org.apache.catalina.Container,你可以在tomcat的聯結器中使用setContainer方法傳遞一個容器給聯結器。
Container介面
第一件需要注意的是在概念上tomcat有四種容器:
- Engine 代表整個Catalina的servlet引擎。
- Host 代表擁有若干個上下文的虛擬主機
- Context 代表一個web應用,一個Context擁有若干個wrapper
- Wrapper 代表一個單獨的servlet
上述的每一個容器都在org.apache.catalina包中有對應的介面。這些介面都繼承自Container,而StandardEngine, StandardHost, StandardContext, and StandardWrapper分別是他們的標準實現類
下圖是他們的UML圖:
一個“上層”容器可以擁有0個或者多個“下層容器”作為child,如一個Context可以擁有多個Wrapper ,但是Wrapper 作為作為最底層容器,不能擁有child。Container介面對於child進行了以下支援:
public void addChild(Container child);
public void removeChild(Container child);
public Container findChild(String name);
public Container[] findChildren();
一個容器也可以擁有其他的元件,比如Loader,Logger,Manager, Realm, and Resources這些將在以後的章節討論。
更有趣的是 Container 介面被設計成 Tomcat 管理員可以通過server.xml 檔案配置來決定其工作方式的模式它通過一個 pipeline(流水線)和一系列的閥門來實現,這些內容將會在下一節 Pipelining Task 中討論。
Pipelining Tasks(流水線任務)
在這裡主要要討論的是其中的四個介面Pipeline(任務流), Valve(閥門), ValveContext, and Contained。
一個任務流包含了該容器要執行的所有任務。一個閥門表示一個特定的任務,在任務流中有一個最基本的閥門,同時你也可以根據需要任意新增閥門,閥門的數目定義為新增的閥門的個數(不包括基本閥門)。閥門可以通過編輯server.xml來自己新增。
如果你已經理解了servlet過濾器,那麼任務流和它的閥門的工作方式不難想象。任務流就像過濾器鏈而閥門就像過濾器,跟過濾器一樣,一個閥門可以操作傳遞給它的 request 和 response 方法。讓一個閥門完成了處理,則進一步處理流水線中的下一個閥門,基本閥門總是在最後才被呼叫。
一個容器可以有一個流水線。當容器的 invoke 方法被呼叫的時候,容器將會處理流水線中的閥門,並一個接一個的處理,直到所有的閥門都被處理完畢。可以想象流水線的 invoke 方法的虛擬碼如下所示:
// 執行新增的閥門
for (int n=0; n<valves.length; n++) {
valve[n].invoke( ... );
}
// 執行基本閥門
basicValve.invoke( ... );
但是, Tomcat 的設計者選擇了一種不同的通過org.apache.catalina.valves.ValveBase定義的方式來處理,在org.apache.catalina.valves包下,定義了各種不同的valves.他們繼承自ValveBase,實現Valve中的invoke方法。在建立一個Valve物件之後,閥門呼叫getNext繼續執行下一個方法。
現在來看看各介面的細節:
The Pipeline Interface 任務流介面
public interface Pipeline
{
public Valve getBasic();
public void setBasic(Valve paramValve);
public void addValve(Valve paramValve);
public Valve[] getValves();
public void removeValve(Valve paramValve);
public Valve getFirst();
public boolean isAsyncSupported();
public Container getContainer();
public void setContainer(Container paramContainer);
}
The Valve Interface 閥門介面
public interface Valve
{
public Valve getNext();
public void setNext(Valve paramValve);
public void backgroundProcess();
public void invoke(Request paramRequest, Response paramResponse)
throws IOException, ServletException;
public boolean isAsyncSupported();
}
The Contained Interface
一個閥門可以選擇性的實現 org.apache.catalina.Contained 介面。該介面定義了其實現類跟一個容器相關聯
public interface Contained
{
public Container getContainer();
public void setContainer(Container paramContainer);
}
Wrapper介面
org.apache.catalina.Wrapper 介面表示了一個包裝器。一個包裝器是表示一個獨立 servlet 定義的容器。包裝器繼承了 Container 介面,並且加了幾個方法,包裝器的實現類負責管理其下層 servlet 的生命週期,包括 servlet 的init,service,和 destroy 方法。由於包裝器是最底層的容器,所以不可以將子容器新增給它。
包裝器介面中重要方法有 allocate 和 load 方法。 allocate 方法負責定位該包裝器表示的 servlet 的例項。 Allocate 方法必須考慮一個 servlet 是否實現了javax.servlet.SingleThreadModel 介面,該部分內容將會在 11 章中進行討論。Load 方法負責 load 和初始化 servlet 的例項。
Context介面
一個 context 在容器中表示一個 web 應用。一個 context 通常含有一個或多個包裝器作為其子容器。重要的方法包括addWrapper,createWrapper 等方法。