首先明確Tomcat的職責是什么,也就是說Tomcat做了哪些事情?
我覺得下面幾件事情是Tomcat必須要做的比較重要的事情。
- 監聽某個端口,捕獲HTTP請求。
- 將HTTP請求轉換為相應的Request對象,當然也應該需要創建一個Response對象
- 然后初始化一個Servlet對象,或者說加載一個Servlet對象,將相應的Request對象和Response對象傳遞進去,之后調用Servlet中的service函數。
Tomcat的架構圖
上面一張圖片比較詳細,下面放一張簡單一點的架構圖。
Tomcat的架構淺析
通過圖片可以看到Tomcat是分成好幾個模塊的。最外面是一個Server,一個Server可以有好幾個Service服務,一個Service服務會有幾個Connector組件,但是只會有一個Container組件,另外還有好幾個不是那么核心的組件,比如說管理Session的組件,寫日志的組件。
其中也提到了最核心的組件(Component)就是Connector和Container。
Connector的作用就是監聽某一個端口,然后接受HTTP請求,將HTTP請求封裝成Servlet需要使用的HttpServletRequest對象和HttpServletResponse對象。也就是說將純文本的Http請求解析出來了之后變成可以方便使用的Request對象,而Response對象需要作為一個返回值的容納地,也是需要傳遞給Servlet*的。
Container的作用就是根據Request中URL中的信息加載相應的Servlet對象,之后就是調用相應的方法,講結果封裝在Response對象中,最后當然也是轉換為HTTP的文本返回給客戶端。
A Simple Servlet Container
當有了基本的架構概念了之后,我覺得可以直接看一些簡單的小例子,來看一下在代碼中這些組件是如何關聯作用在一起的,下面就把How Tomcat Works中的一個第五章中的小例子貼出來。(其實前面幾個章節有更加簡單的例子,感興趣的可以看下。)
package ex05.pyrmont.startup;
import ex05.pyrmont.core.SimpleContext;
import ex05.pyrmont.core.SimpleContextMapper;
import ex05.pyrmont.core.SimpleLoader;
import ex05.pyrmont.core.SimpleWrapper;
import ex05.pyrmont.valves.ClientIPLoggerValve;
import ex05.pyrmont.valves.HeaderLoggerValve;
import org.apache.catalina.Context;
import org.apache.catalina.Loader;
import org.apache.catalina.Mapper;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
public final class Bootstrap2 {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
//繼承了Wrapper,每一個實例里面都是會有一個Servlet的
Wrapper wrapper1 = new SimpleWrapper();
//設置Servlet的映射地址
wrapper1.setName("Primitive");
//設置Servlet的名字
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
//context是一個容器可以包含wrapper這個最底層的容器
Context context = new SimpleContext();
context.addChild(wrapper1);
context.addChild(wrapper2);
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
//容器中除了其他容器之外還有Valve
//另外要注意的是每一個context都是實現了Pipeline和Context接口的
((Pipeline) context).addValve(valve1);
((Pipeline) context).addValve(valve2);
//這個mapper是做什么的呢?
Mapper mapper = new SimpleContextMapper();
mapper.setProtocol("http");
context.addMapper(mapper);
Loader loader = new SimpleLoader();
//容器中還需要加載器,通過反射加載真正的Servlet對象
context.setLoader(loader);
// context.addServletMapping(pattern, name);
//context里面初始化了一個HashMap,存儲映射和Servlet名字
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
//因為connector封裝好Reqeust之后會調用容器,所以將容器的聲明給Connector
connector.setContainer(context);
try {
connector.initialize();
//connector開始監聽端口,要明白底層肯定使用ServerSocket來實現的
connector.start();
// make the application wait until we press a key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
BootStrap是一個Tomcat啟動的程序入口。可以看到源代碼中初始化了這么幾個對象,一個HttpConnector,兩個Wrapper,一個Context,兩個Valve,一個Loader等,具體可以看注釋,還是把我的理解寫在注釋里面把。
再看一張圖
從第一張架構圖中可以看到container是分成下面幾個容器的,engine
,host
,context
,wrapper
。關系是這樣的一個engine
是可以有零個或者多個host
的,以此類推。
那么這些容器的作用是什么呢?其實是用來匹配相應的URL,也就是講一個請求的URL來分成者三個部分,最后找到要請求的Servlet。也就是說request
是不斷地在容器中傳遞的。其實容器中還有一個叫做Pipeline
的東西,暫時還是不是很明白這個東西的作用是什么,每一個Pipeline
都有很多的Valve
。(有一點是這樣的,Pipeline
其實和過濾器是一個很像的東西)
ServletContext在哪里呢?
其實就是StandardContext中的一個全局變量。
Tomcat8.0.37中StandardContext.java
部分源碼
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter
/**
* The ServletContext implementation associated with this Context.
*/
protected ApplicationContext context = null;
/**
* Return the servlet context for which this Context is a facade.
*/
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return (context.getFacade());
}
最后
其實現在如果是用Springmvc這個框架的話,其實只是在web.xml中配置了一個攔截所有請求的前端Servlet,但是之后的事情比如Controller之類的東西都是Sringmvc在做了,而不是最原始的一個請求對應一個Servlet了。
最后的最后
因個人能力有限,以上內容肯定會有錯誤,所有歡迎交流討論,另外如果覺得有幫助,可以點擊一個喜歡,將這篇文章獻給以后的自己和看到最后的你。
參考
JavaWeb學習之Servlet(四)----ServletConfig獲取配置信息、ServletContext的應用
己
Tomcat容器結構及Pipeline機制 -我們到底能走多遠系列(13)
tomcat架構分析概覽
粗淺看 Tomcat系統架構分析
How do servlets work? Instantiation, sessions, shared variables and multithreading
Tomcat源碼分析之容器整體結構