Tomcat是Servlet最受歡迎的容器之一,他們之間彼此依存,為了解耦,通過標準化接口相互協(xié)作。
Tomcat的核心組件是Connector和Container.其中,Connector組件是可以被替換的,這樣就給設計者提供了比較靈活的設計模式,一個Container可以對應多個Connector,它們一起組成了一個Servcie,就可以對外提供服務了。Servcie還要一個生存環(huán)境,那就是Server。整個Tomcat的生命周期是由Server控制的。

Tomcat容器分為4個等級:
Container 容器 ==> Engine容器 ==> Host ==> Context

以Servcie作為“婚姻”
Container:小仙女 Connector:男生
他們是一對快樂的情侶,Connector作為男生,平時主要負責對外交流,Container小仙女主要負責內(nèi)部的事物,他們兩彼此精誠合作,生活也是井井有條。
他們兩有了Service這個結婚證呢,就是受外界承認的小夫妻了,無論是在法律上還是生活形式上,他們都是一體的了。

為了讓生活更加豐富多彩,他們更加細致的規(guī)劃了自己的生活,就是StandardService,同時實現(xiàn)了Service接口,還實現(xiàn)了Lifecycle接口。
setContainer方法的源碼如下:
@Override
public void setContainer(Engine engine) {
Engine oldEngine = this.engine;
if (oldEngine != null) {
oldEngine.setService(null);
}
this.engine = engine;
if (this.engine != null) {
this.engine.setService(this);
}
if (getState().isAvailable()) {
if (this.engine != null) {
try {
this.engine.start();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.engine.startFailed"), e);
}
}
// Restart MapperListener to pick up new engine.
try {
mapperListener.stop();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.mapperListener.stopFailed"), e);
}
try {
mapperListener.start();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.mapperListener.startFailed"), e);
}
if (oldEngine != null) {
try {
oldEngine.stop();
} catch (LifecycleException e) {
log.warn(sm.getString("standardService.engine.stopFailed"), e);
}
}
}
// Report this property change to interested listeners
support.firePropertyChange("container", oldEngine, this.engine);
}
這段代碼邏輯很簡單,首先判斷當前這個Service有沒有關聯(lián)Container,如果已經(jīng)有關聯(lián),就去掉——>oldEngine.setService(null);如果這個Container已經(jīng)啟動了,則結束它的生命周期——>oldEngine.stop();然后再啟動新的關聯(lián)、初始化并開始這個新的Container得生命周期。
addContainer方法的源碼如下:
@Override
public void addConnector(Connector connector) {
synchronized (connectorsLock) {
connector.setService(this); //設置關聯(lián)關系
Connector results[] = new Connector[connectors.length + 1]; //初始化工作
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results;
if (getState().isAvailable()) {
try {
connector.start();
} catch (LifecycleException e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
// Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
}
}
以Server為“居”
既然Container小仙女和Connector有了合法的夫妻關系,那么他們也需要一個基本的條件去維系這段“婚姻”,他們需要一個實體的家,Server.
Server要完成的任務很簡答,就是提高一個接口讓其它程序可以訪問這個Service集合,同時也要維護他所包含的Service的生命周期。
StandardServer是他的標準實現(xiàn)類,同時也繼承了LifecycleMBeanBase。
addService方法源碼如下:
@Override
public void addService(Service service) {
service.setServer(this); //由此可以看出Service和Server是相互關聯(lián)的
synchronized (servicesLock) {
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
// Report this property change to interested listeners
support.firePropertyChange("service", null, service);
}
}
組件的生命線“Lifecycle”
Tomcat中組件的生命周期都是由Lifecycle接口來控制的,組件只要繼承這個接口并實現(xiàn)其中的方法就可以統(tǒng)一被擁有他的組件控制了。這樣一層一層到最高級的組件就可以控制Tomcat中所有組件的生命周期了,這個最高級的組件就是Server,控制Server的是Startup,也就是啟動和關閉Tomcat.

除了控制生命周期的start和stop方法外,還有一個監(jiān)聽機制,在生命周期開始和結束時做一些額外的操作。
Lifecycle接口實現(xiàn)都在其它組件中。
負責外部交流的Connector
接受瀏覽器發(fā)過來的TCP請求,創(chuàng)建一個Request和Response對象分別用于請求端交換數(shù)據(jù),然后將產(chǎn)生一個線程來處理這個請求并把產(chǎn)生的Request和Response對象傳給處理這個請求的線程,之后處理這個線程就是Container的事情了。
Servlet容器Container
Container是容器的父接口,所有容器必須實現(xiàn)這個接口,設計是典型的責任鏈的設計模式,他有4個容器組成,分別是Engine、Context和Wrapper,這四個組件不是平行的,而是父子關系。通常一個Servlet class對應一個Wrapper.
Engine容器
標準實現(xiàn)類時StandardEngine,Engine沒有父容器了,調(diào)用setParent就會出錯
@Override
public void setParent(Container container) {
throw new IllegalArgumentException
(sm.getString("standardEngine.notParent"));
}
添加子容器也只能是Host類型
@Override
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
Host容器
Host是Engine的子容器,一個Host在Engine中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應用,它負責安裝和展開這些應用,而且進行標識以便于區(qū)分。他的子容器是Context,它除了關聯(lián)子容器外,還有就是保存一個主機應該有的信息。
Context容器
他具備Servlet運行的基本環(huán)境,是在管理Servlet實例,Servlet實例在Context容器中是以Wrapper出現(xiàn)的。
Wrapper容器
Wrapper代表一個Servlet,包括Servlet的裝載、初始化、執(zhí)行和資源回收。是最底層的,沒有再底層的容器了。
loadServlet是個很重要的方法,代碼片段如下:
public synchronized Servlet loadServlet() throws ServletException {
······
Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
}
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
unavailable(null);
if(log.isDebugEnabled()) {
log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
}
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.instantiate", servletClass), e);
}
if (multipartConfigElement == null) {
MultipartConfig annotation =
servlet.getClass().getAnnotation(MultipartConfig.class);
if (annotation != null) {
multipartConfigElement =
new MultipartConfigElement(annotation);
}
}
processServletSecurityAnnotation(servlet.getClass());
// Special handling for ContainerServlet instances
if ((servlet instanceof ContainerServlet) &&
(isContainerProvidedServlet(servletClass) ||
((Context) getParent()).getPrivileged() )) {
((ContainerServlet) servlet).setWrapper(this);
}
classLoadTime=(int) (System.currentTimeMillis() -t1);
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
}
singleThreadModel = true;
}
initServlet(servlet);
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
if (swallowOutput) {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet;
}
Servlet容器的工作原理

由上圖可知,Servlet規(guī)范就是基于上面的幾個類運轉的,與Servlet主動關聯(lián)的是三個類:ServletConfig、ServletRequest、ServletResponse,這3個類都是通過容器傳遞給Servlet的。
用戶從瀏覽器向服務器發(fā)起一個請求通常會包含如下信息:
http://hostname:port/contextpath/servletpath
hostname:port用來與服務器建立TCP連接,而后面的URL才用來選擇服務器的哪個子容器服務用戶的請求。
這種映射關系專門由一個類來完成,這個類就是org.apache.servlet.util.http.mapper,這個類保存了Tomcat的Container所有子容器的信息。
- 創(chuàng)建一個Context容器,很重要的一個配置是ContextConfig,負責整個Web應用的配置文件解析工作。
- Context init(一個Context對應一個web),Context容器的Listener將被調(diào)用,ContextConfig調(diào)用了LifecycleListener接口。
- Context執(zhí)行startInternal方法
- Web應用的初始化工作,ContextConfig中的configureStart方法實現(xiàn)。
- 創(chuàng)建Servlet對象
- 初始化Servlet
Servlet中的url-pattern
匹配順序:精確匹配 路徑匹配 后綴匹配
程序媛小白一枚,如有錯誤,煩請批評指正!(#.#)