Tomcat是Servlet最受歡迎的容器之一愈捅,他們之間彼此依存咧最,為了解耦,通過標(biāo)準(zhǔn)化接口相互協(xié)作阎肝。
Tomcat的核心組件是Connector和Container.其中挤渔,Connector組件是可以被替換的,這樣就給設(shè)計者提供了比較靈活的設(shè)計模式风题,一個Container可以對應(yīng)多個Connector判导,它們一起組成了一個Servcie,就可以對外提供服務(wù)了沛硅。Servcie還要一個生存環(huán)境眼刃,那就是Server。整個Tomcat的生命周期是由Server控制的摇肌。
Tomcat容器分為4個等級:
Container 容器 ==> Engine容器 ==> Host ==> Context
以Servcie作為“婚姻”
Container:小仙女 Connector:男生
他們是一對快樂的情侶擂红,Connector作為男生,平時主要負(fù)責(zé)對外交流围小,Container小仙女主要負(fù)責(zé)內(nèi)部的事物昵骤,他們兩彼此精誠合作,生活也是井井有條肯适。
他們兩有了Service這個結(jié)婚證呢变秦,就是受外界承認(rèn)的小夫妻了,無論是在法律上還是生活形式上框舔,他們都是一體的了蹦玫。
為了讓生活更加豐富多彩,他們更加細(xì)致的規(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);
}
這段代碼邏輯很簡單纬凤,首先判斷當(dāng)前這個Service有沒有關(guān)聯(lián)Container福贞,如果已經(jīng)有關(guān)聯(lián),就去掉——>oldEngine.setService(null);如果這個Container已經(jīng)啟動了停士,則結(jié)束它的生命周期——>oldEngine.stop();然后再啟動新的關(guān)聯(lián)肚医、初始化并開始這個新的Container得生命周期绢馍。
addContainer方法的源碼如下:
@Override
public void addConnector(Connector connector) {
synchronized (connectorsLock) {
connector.setService(this); //設(shè)置關(guān)聯(lián)關(guā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有了合法的夫妻關(guān)系向瓷,那么他們也需要一個基本的條件去維系這段“婚姻”肠套,他們需要一個實體的家,Server.
Server要完成的任務(wù)很簡答猖任,就是提高一個接口讓其它程序可以訪問這個Service集合你稚,同時也要維護他所包含的Service的生命周期。
StandardServer是他的標(biāo)準(zhǔn)實現(xiàn)類朱躺,同時也繼承了LifecycleMBeanBase刁赖。
addService方法源碼如下:
@Override
public void addService(Service service) {
service.setServer(this); //由此可以看出Service和Server是相互關(guān)聯(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,也就是啟動和關(guān)閉Tomcat.
除了控制生命周期的start和stop方法外,還有一個監(jiān)聽機制源请,在生命周期開始和結(jié)束時做一些額外的操作枪芒。
Lifecycle接口實現(xiàn)都在其它組件中。
負(fù)責(zé)外部交流的Connector
接受瀏覽器發(fā)過來的TCP請求谁尸,創(chuàng)建一個Request和Response對象分別用于請求端交換數(shù)據(jù)舅踪,然后將產(chǎn)生一個線程來處理這個請求并把產(chǎn)生的Request和Response對象傳給處理這個請求的線程,之后處理這個線程就是Container的事情了良蛮。
Servlet容器Container
Container是容器的父接口抽碌,所有容器必須實現(xiàn)這個接口,設(shè)計是典型的責(zé)任鏈的設(shè)計模式决瞳,他有4個容器組成货徙,分別是Engine、Context和Wrapper,這四個組件不是平行的皮胡,而是父子關(guān)系痴颊。通常一個Servlet class對應(yīng)一個Wrapper.
Engine容器
標(biāo)準(zhǔn)實現(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中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應(yīng)用烹笔,它負(fù)責(zé)安裝和展開這些應(yīng)用裳扯,而且進行標(biāo)識以便于區(qū)分。他的子容器是Context,它除了關(guān)聯(lián)子容器外谤职,還有就是保存一個主機應(yīng)該有的信息饰豺。
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ī)范就是基于上面的幾個類運轉(zhuǎn)的捺癞,與Servlet主動關(guān)聯(lián)的是三個類:ServletConfig夷蚊、ServletRequest、ServletResponse,這3個類都是通過容器傳遞給Servlet的髓介。
用戶從瀏覽器向服務(wù)器發(fā)起一個請求通常會包含如下信息:
http://hostname:port/contextpath/servletpath
hostname:port用來與服務(wù)器建立TCP連接惕鼓,而后面的URL才用來選擇服務(wù)器的哪個子容器服務(wù)用戶的請求。
這種映射關(guān)系專門由一個類來完成唐础,這個類就是org.apache.servlet.util.http.mapper,這個類保存了Tomcat的Container所有子容器的信息箱歧。
- 創(chuàng)建一個Context容器,很重要的一個配置是ContextConfig彻犁,負(fù)責(zé)整個Web應(yīng)用的配置文件解析工作叫胁。
- Context init(一個Context對應(yīng)一個web),Context容器的Listener將被調(diào)用,ContextConfig調(diào)用了LifecycleListener接口汞幢。
- Context執(zhí)行startInternal方法
- Web應(yīng)用的初始化工作驼鹅,ContextConfig中的configureStart方法實現(xiàn)。
- 創(chuàng)建Servlet對象
- 初始化Servlet
Servlet中的url-pattern
匹配順序:精確匹配 路徑匹配 后綴匹配
程序媛小白一枚森篷,如有錯誤输钩,煩請批評指正!(#.#)