大家好,我是小米,一個在代碼裏泡了九年的程序員。前幾天去面試一家互聯網公司,面試官笑眯眯地問我:“你瞭解 Tomcat 的 Container 架構嗎?”
我心想,這問題挺常見的,屬於那種“問不深就考概念、問深了就勸退”的經典類型。於是我開始講——沒想到,這一講,就聊了快半個小時。
今天就把我當時的回答完整覆盤給你聽,保證聽完後,你不再怕面試官問這個問題。
故事開頭:Tomcat 是一座“服務城市”
我特別喜歡把 Tomcat 比作一座城市。
在這座“城市”裏,有各種基礎設施:
- Connector(連接器):它就像城市的“交通入口”,負責接待外來的請求(HTTP 或 AJP)。
- Container(容器):它是城市的“核心區域”,負責處理這些請求、執行 Servlet、返回結果。
可以這麼説,Connector 接收請求,Container 處理請求。
而 Container 的內部,其實是一個層層嵌套、各司其職的“小宇宙”。
如果把它比作一座行政體系,那它大概長這樣:
Engine(發動機) → Host(主機) → Context(上下文) → Wrapper(包裝器)
聽起來有點抽象?別急,我們慢慢拆。
Engine:一切請求的總管
Engine 是整個 Container 體系的頂層容器,就像一座城市的“市政府”。
當 Connector 把請求交給 Engine 時,Engine 要決定:
- 這個請求該交給哪台主機(Host)?
- 用哪個 Web 應用(Context)去處理?
- 最後哪個具體的 Servlet(Wrapper)來執行
Tomcat 默認只有一個 Engine,名字叫 Catalina(這名字是不是很熟?Tomcat 的主類就叫這個)。
Engine 內部會註冊多個 Host,比如:
- localhost
- myapp.company.com
- test.company.com
Engine 根據請求頭裏的 Host 值去匹配對應的 Host。
換句話説,Engine 是整個容器鏈的入口,負責路由請求到正確的主機。
Host:承載多個 Web 應用的主機
每個 Host 代表一台虛擬主機,也就是一個域名或主機名。
比如我們訪問:
http://localhost:8080/demo
這裏的 localhost 對應的就是一個 Host。
Host 的職責是:
- 管理多個 Web 應用(Context)
- 為每個應用分配獨立的 ClassLoader(類加載空間)
- 隔離不同應用間的資源
每個 Host 下可以部署多個 Context,例如:
/demo /shop /blog
Host 的核心思想是“多租户”——不同項目可以共存而互不干擾。
Context:Web 應用的靈魂
如果説 Host 是一台服務器,那 Context 就是一款部署在服務器上的 Web 應用。
Context 是最重要的容器之一,它直接對應一個 Web 項目(比如 webapps/demo 目錄)。
Context 的職責包括:
- 管理 Servlet、Filter、Listener 等組件;
- 負責加載 web.xml、初始化應用;
- 負責分發請求到具體的 Servlet。
舉個例子,當我們訪問 /demo/hello 時:
- Engine 根據主機名找到 Host;
- Host 根據 /demo 找到對應的 Context;
- Context 再去定位具體的 Servlet(Wrapper)。
可以看到,Context 是實際業務處理的舞台,所有業務邏輯、配置、過濾器、攔截器都在這一級展開。
Wrapper:Servlet 的“外衣”
Wrapper 是 Container 體系中最底層、最貼近業務的一層。
它包裹着具體的 Servlet,比如 HelloServlet 或 UserLoginServlet。
Wrapper 負責:
- 管理 Servlet 的生命週期(加載、初始化、銷燬);
- 處理線程安全;
- 調用真正的 service() 方法。
當請求一路從 Engine → Host → Context → Wrapper 傳遞下來時,最終在 Wrapper 層執行具體的 Servlet。
所以,Wrapper 是執行請求的最後一站。
四層容器的層級關係總結
我們用一個簡單的結構來表示它:
這四層是典型的“組合模式”(Composite Pattern),每一層都是 Container 的子類,都實現了 org.apache.catalina.Container 接口。
Tomcat 用這個設計實現了一個可擴展、可遞歸的容器層級體系——上層管理下層,下層專注具體邏輯。
Container 的核心接口設計
Tomcat 的 Container 架構其實高度面向接口設計,主要接口包括:
- Container:所有容器的頂層接口;
- Engine、Host、Context、Wrapper:四個具體子接口;
- Pipeline / Valve:責任鏈機制,用於在請求處理前後執行攔截邏輯;
- Lifecycle:生命週期管理接口。
當我們理解這些接口後,就能明白 Tomcat 的設計精髓:
它把請求的處理過程抽象成一個管道(Pipeline),讓每個 Valve 都能插入處理邏輯。
這就像在高速公路上設置多個收費站、檢查站,每個環節都能“攔一攔、看一看、做點事”。
Pipeline 與 Valve:Tomcat 的責任鏈魔法
在每個容器中,都有一個 Pipeline(管道),裏面裝着一連串的 Valve(閥門)。
請求進入時,會依次流過這些 Valve,每個 Valve 可以做:
- 日誌記錄
- 權限驗證
- 過濾器處理
- 性能統計
- 最終調用下一級容器
比如在 Context 層的 Pipeline 中,常見的一個 Valve 是 StandardContextValve,它的作用是:
- 找到請求對應的 Wrapper;
- 調用 Wrapper 裏的 Servlet。
而在 Engine 層、Host 層,也都有各自的 Valve,比如:
- ErrorReportValve:統一異常處理;
- AccessLogValve:記錄訪問日誌。
這個機制讓 Tomcat 非常靈活,你可以輕鬆在任意層插入自定義邏輯。
請求在 Container 中的流轉之旅
讓我們以一次完整的請求為例,看看它的旅程:
1、Connector 接收請求
- 監聽 8080 端口;
- 解析 HTTP 協議;
- 生成 Request 和 Response 對象;
- 把它交給 Engine。
2、Engine 匹配 Host
- 根據請求頭裏的 Host 字段找到對應的虛擬主機。
3、Host 匹配 Context
- 根據 URI 前綴(如 /demo)找到對應的應用。
4、Context 匹配 Wrapper
- 根據 Servlet 映射規則找到目標 Servlet。
5、Wrapper 調用 Servlet
- 通過 Pipeline 調用 Servlet 的 service() 方法;
- 最終執行我們的業務邏輯;
- 返回響應。
整個流程,就像一場“層層轉發的旅程”:
Connector → Engine → Host → Context → Wrapper → Servlet
面試官可能追問的進階問題
別以為講完層級關係就結束了,面試官很可能接着問:
“那 Container 和 Connector 之間怎麼通信?”
答:通過 Mapper 映射組件。
Mapper 根據請求 URL、主機名、路徑來定位目標容器(Host → Context → Wrapper)。
Connector 解析完請求後,會調用 Engine.getPipeline().getFirst().invoke(request, response),啓動整個責任鏈。
“每層容器如何實現解耦?”
答:Tomcat 使用接口 + 事件驅動機制,每個容器都實現 Lifecycle 接口,可以獨立啓動、銷燬、重新加載。這讓熱部署、應用隔離變得可行。
寫在最後:Tomcat 的“優雅之處”
當年第一次看 Tomcat 源碼,我差點被繞暈。
但當我真正理解 Container 的設計後,才發現它的優雅之處在於:
- 用組合模式實現層級容器;
- 用責任鏈實現靈活攔截;
- 用生命週期接口實現可插拔模塊;
- 用映射機制實現精準路由。
這不是“寫一堆類”的架構,而是一個能應對上億次請求的分層系統。
所以,當面試官問你“Tomcat 的 Container 架構”時,千萬別隻背誦那四層。要講出它們為什麼這樣設計、怎麼協作、能帶來什麼好處。
彩蛋:一句話總結送給你
Connector 負責“接”,Container 負責“處”。
Container 體系分四層:Engine、Host、Context、Wrapper。
每層用 Pipeline + Valve 實現責任鏈,
這就是 Tomcat 的“容器心臟”。
END
下次再有人問你這個問題,就別怕了。
你可以微微一笑,説:“Tomcat 的 Container,我太熟了——它其實是一場層層嵌套的優雅設計。”
如果你覺得這篇文章有點幫助,點個“在看”吧,我會繼續更新 Java 面試題系列,用故事幫你吃透底層源碼!
我是小米,一個喜歡分享技術的31歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術乾貨!