Tomcat源码系列文章
Tomcat源码解析(二):Bootstrap和Catalina
Tomcat源码解析(四):StandardServer和StandardService
Tomcat源码解析(五):StandardEngine、StandardHost、StandardContext、StandardWrapper
前文中我们介绍了StandServer与StandService的init与start方法,而Service的init方法和start方法则是调用顶级容器Engine、请求url映射Mapper、连接器Connector的init和start方法。本文就介绍下容器的init和start及Mapper的组成。
容器接口与实现类的类图如下:
Wrapper
:表示一个Servlet
,它负责管理Servlet的生命周期 Context
:表示一个Web应用程序
,是Servlet、Filter的父容器 Host
:表示一个虚拟主机
,包含主机名称和IP地址,默认是localhost Engine
:表示顶级容器
,接受连接器的所有请求,并将响应返回给连接器 Context和Host的区别
Context表示一个应用,比如,默认配置下webapps下的每个目录都是一个应用
,其中ROOT目录中存放着主应用,其他目录存放着别的子应用。
而整个webapps是一个Host(站点,有名虚拟主机)。假如www.excelib.com域名对应着webapps目录所代表的站点,其中的ROOT目录里的应用就是主应用,访问时直接使用域名就可以,而webapps/test目录存放的是test子应用,访问时需要www.excelib.com/test,每一个应用对应一个Context ,所有webapps下的应用都属于www.excelib.com站点,而blog.excelib.com则是另外一个站点,属于另外一个Host。
public interface Container extends Lifecycle {
public Container getParent();
public void setParent(Container container);
public void addChild(Container child);
public Container[] findChildren();
public void removeChild(Container child);
}
Engine的实现类为StandardEngine,在前文中我们了解到StandardService在init时调用了engine.init()来初始化engine,StandardEngine没有重写Init方法,但由于StandardEngine继承了ContainerBase从而间接继承了LifecycleBase抽象类,得以复用LifecycleBase中的init方法
,start方法也是如此,因此以engine为代表的这些子容器实际上只需要重写initInternal、startInternal即可。
有且只有一个
<!--
name: ⽤于指定Engine 的名称, 默认为Catalina
defaultHost:默认使⽤的虚拟主机名称, 当客户端请求指向的主机⽆效时,
将交由默认的虚拟主机处理, 默认为localhost
-->
<Engine name="Catalina" defaultHost="localhost">
...
</Engine>
# Catalina#createStartDigester方法
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
setContainer
方法将StandardEngine对象设置到Service的engine属性中# EngineRuleSet#addRuleInstances方法
// prefix = "Server/Service/"
/** 解析<Engine>标签实例化StandardEngine对象 **/
digester.addObjectCreate(prefix + "Engine",
"org.apache.catalina.core.StandardEngine",
"className");
/** 解析<Engine>标签将标签中属性值映射到StandardEngine对象中 **/
digester.addSetProperties(prefix + "Engine");
/** 将StandardEngine对象通过Service的setContainer设置到Service的engine属性中 **/
digester.addSetNext(prefix + "Engine",
"setContainer",
"org.apache.catalina.Engine");
@Override
protected void initInternal() throws LifecycleException {
// Realm:安全认证的,不用管
getRealm();
super.initInternal();
}
线程池
,将会在下文用于启动子容器
,这样可以用多个线程来同时启动,效率更高
protected ThreadPoolExecutor startStopExecutor;
@Override
protected void initInternal() throws LifecycleException {
// 创建线程安全的队列
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
// 创建线程池
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
// 调用父类LifecycleMBeanBase的初始化方法
super.initInternal();
}
protected synchronized void startInternal() throws LifecycleException {
if (log.isInfoEnabled()) {
log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
}
super.startInternal();
}
请求
时候,依次经过每个容器的Pipeline,做一些处理,后面章节讲protected synchronized void startInternal() throws LifecycleException {
// Cluster 用于配置集群,在server.xml 中有注释的参考配置,它的作用就是同步Session
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
// Realm 是Tomcat 的安全域,可以用来管理资源的访问权限
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// 将要启动的子容器加入到线程池中异步启动
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {
results.add(startStopExecutor.submit(new StartChild(child)));
}
// 省略获取子容器启动结果results,正常启动返回null,如果返回结果则证明异常,抛出错误
...
// 子容器启动完成后接着启动容器的管道,管道下篇文章中详细讲解
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 设置状态为启动中,这里修改状态是为了触发监听器
setState(LifecycleState.STARTING);
// 启动后台线程,该线程将定期检查会话超时
threadStart();
}
// 启动子容器的线程类型StartChild 是一个实现了Callable 的内部类,主要作用就是调用子容器的start 方法
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}
// ContainerBase的stopInternal方法
@Override
protected synchronized void stopInternal() throws LifecycleException {
// 停止定期检查会话超时的后台线程
threadStop();
// 设置状态为停止中
setState(LifecycleState.STOPPING);
// 调用容器管道的关闭方法
if (pipeline instanceof Lifecycle &&
((Lifecycle) pipeline).getState().isAvailable()) {
((Lifecycle) pipeline).stop();
}
// 调用子容器的stop方法
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StopChild(children[i])));
}
// 省略容器关闭结果处理,如果关闭中出现错误结果results,抛出异常
...
// 停止Realm和Cluster
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).stop();
}
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).stop();
}
}
// ContainerBase的destroyInternal方法
@Override
protected void destroyInternal() throws LifecycleException {
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).destroy();
}
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).destroy();
}
// 调用容器管道的销毁方法
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).destroy();
}
// 获取所有的子容器,调用子容器的销毁方法
for (Container child : findChildren()) {
removeChild(child);
}
// 从父容器中移除子容器,并调用当前容器的销毁方法
if (parent != null) {
parent.removeChild(this);
}
// 中断所有工作线程,并清空工作队列,拒绝新提交的任务,
if (startStopExecutor != null) {
startStopExecutor.shutdownNow();
}
// 父类LifecycleMBeanBase的方法,jmx内容
super.destroyInternal();
}
Host是Engine的子容器。Engine组件中可以内嵌一个或多个Host组件
,每个Host组件代表Engine中的一个虚拟主机。Host组件至少有一个,且其中一个的name必须与Engine组件的defaultHost属性相匹配。
<!--
name:虚拟主机名。一个Engine中有且仅有一个Host组件的name属性与Engine组件的defaultHost属性相匹配
unpackWARs:是否将代表Web应用的WAR文件解压;
true:通过解压后的文件结构运行该Web应用;
false:直接使用WAR文件运行web应用
autoDeploy和、APPBase、xmlBase、deployOnStartup属性,与Host内Web应用的自动部署有关
-->
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
...
</Host>
# Catalina#createStartDigester方法
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
addChild
方法将StandardHost对象设置到StandardEngine的父类ContainerBase的children的map集合属性中HostConfig监听器
添加到Host的父类LifecycleBase的监听器集合lifecycleListeners中,很重要下面讲# HostRuleSet#addRuleInstances方法
// prefix = "Server/Service/Engine/"
/** 解析<Host>标签实例化StandardHost对象 **/
digester.addObjectCreate(prefix + "Host",
"org.apache.catalina.core.StandardHost",
"className");
/** 将HostConfig添加到Host的父类LifecycleBase的监听器集合lifecycleListeners中 **/
digester.addRule(prefix + "Host",
new LifecycleListenerRule
("org.apache.catalina.startup.HostConfig",
"hostConfigClass"));
/** 解析<Host>标签将标签中属性值映射到StandardHost对象中 **/
digester.addSetProperties(prefix + "Host");
/** 通过StandardEngine的addChild方法设置到Engine的children属性中 **/
digester.addSetNext(prefix + "Host",
"addChild",
"org.apache.catalina.Container");
StandardHost没有重写initlntemal 方法,初始化默认调用ContainerBase的initlntemal方法,ContainerBase的initlntemal方法上面说过了,就是创建了一个线程池,用于启动子容器,StandardEngine创建线程池用于启动子容器StandardHost,StandardHost也创建线程池用于启动子容器StandardContext(下面会讲)。
核心内容调用父类ContainerBase的startInternal方法上面说过了,就是使用线程池异步调用子容器的start方法,子容器启动完成后接着启动容器的管道。
@Override
protected synchronized void startInternal() throws LifecycleException {
// 省略检查错误报告Class
...
super.startInternal();
}
StandardHost与StandardEngine一样,都没有重新stopInternal和destroyInternal方法,默认调用ContainerBase的stopInternal和destroyInternal方法。
在前面章节LifeCycle生命周期管理中讲过,生命周期状态转化成功之后会触发事件,如下setStateInternal转化状态的公共方法,初始化、启动前中后都会切换状态
,调用如下公共方法,触发事件实际就是调用监听器的lifecycleEvent方法
。
// LifecycleBase类属性和方法
private volatile LifecycleState state = LifecycleState.NEW;。
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
// 是否校验状态,一般初始化启动流程这里都是false
if (check) {
// state不允许为null
if (state == null) {
// 抛异常
invalidTransition("null");
return;
}
if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) {
invalidTransition(state.name());
}
}
// 设置状态
this.state = state;
// 触发事件
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
// HostConfig类方法
@Override
public void lifecycleEvent(LifecycleEvent event) {
...
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
// 定时检查(检查资源修改以触发重新部署、热部署等)
check();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
// 启动前调用,创建webapps目录
beforeStart();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
// 启动中调用
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
// 停止中调用
stop();
}
}
public void start() {
...
// true表示应发现并自动部署此主机的子Web应用
if (host.getDeployOnStartup()) {
deployApps();
}
}
// HostConfig类方法
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
// 如下图,获取webapps下的文件
String[] filteredAppPaths = filterAppPaths(appBase.list());
// 部署XML
deployDescriptors(configBase, configBase.list());
// 部署WAR
deployWARs(appBase, filteredAppPaths);
// 部署扩展的文件夹
deployDirectories(appBase, filteredAppPaths);
}
// HostConfig类方法
protected void deployWARs(File appBase, String[] files) {
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
// 文件夹为META-INF或WEB-INF跳过不处理
if (files[i].equalsIgnoreCase("META-INF")) {
continue;
}
if (files[i].equalsIgnoreCase("WEB-INF")) {
continue;
}
File war = new File(appBase, files[i]);
if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
war.isFile() && !invalidWars.contains(files[i]) ) {
// 这里以war包文件名+/命名,如果springmvc.war,Context path = /springmvc
ContextName cn = new ContextName(files[i], true);
// 服务已部署,文件路径文件名不能包含*和?两个字符,否则跳过不处理
...
results.add(es.submit(new DeployWar(this, cn, war)));
}
}
}
StandardContext
,添加监听器ContextConfig
// HostConfig内部类DeployWar,线程启动调用deployWAR方法
protected String contextClass = "org.apache.catalina.core.StandardContext";
protected void deployWAR(ContextName cn, File war) {
// 省略,只留关键代码
...
// 实例化Context
Context context = (Context) Class.forName(contextClass).getConstructor().newInstance();
// 添加监听器ContextConfig
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
context.addLifecycleListener(listener);
// 设置context属性,name和path这里是/springmvc
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName() + ".war");
// 将context添加到父容器host中并启动context
host.addChild(context);
}
主要内容调用父类ContainerBase的initlntemal方法,ContainerBase的initlntemal方法上面说过了,就是创建了一个线程池,用于启动子容器。
与之前容器不同的是,不再调用父类ContainerBase的startInternal方法,对于子类和容器通道的启动都在StandContext类的startInternal方法中。StandContext启动很复杂,涉及很多知识面,我这里只列举出一些比较重要的点。
//
@Override
protected synchronized void startInternal() throws LifecycleException {
...
// ContextConfig监听器
// 解析web.xml或注解里Servlet、Filter、Listener
// 并创建对应的Wrapper
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// 启动Context子节点Wrapper。
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
// 启动Context的pipeline(下个章节单独将)
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// 将Context的Web资源集合添加到ServletContext。
if (ok) {
getServletContext().setAttribute(Globals.RESOURCES_ATTR, getResources());
}
// 将Jar包扫描器添加到ServletContext。
if (ok) {
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}
// 实例化application域监听器,并执行
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
// 实例化FilterConfig、Filter并调用Filter.init()。
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// 对于loadOnStartup大于等于0的Wrapper,调用Wrapper.load()
// 该方法负责实例化Servlet,并调用Servlet.init()进行初始化。
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
...
}
// ContextConfig类方法
@Override
public void lifecycleEvent(LifecycleEvent event) {
...
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();// Context容器启动中触发调用方法
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
...
}
protected synchronized void configureStart() {
...
// 核心方法,解析web.xml和注解
webConfig();
...
}
web.xml
中的Servlet、Filter、Listener
添加到webXml中/WEB-INF/classes
路径下所有@WebServlet、@WebFilter、@WebListener
添加到webXml中// ContextConfig类方法
protected void webConfig() {
...
// xml和注解里的Servlet、Filter、Listener都会添加到这里
WebXml webXml = createWebXml();
// 获取/WEB-INF/web.xml的资源输入流
InputSource contextWebXml = getContextWebXmlSource();
// 解析xml中的Servlet、Filter、Listener
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// 筛选/WEB-INF/classes路径下所有@WebServlet、@WebFilter、@WebListener
processClasses(webXml, orderedFragments);
}
// 添加wrapper为context的子容器
configureContext(webXml);
...
}
// ContextConfig类方法
protected void processClass(WebXml fragment, JavaClass clazz) {
AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
if (annotationsEntries != null) {
String className = clazz.getClassName();
// 遍历类注解处理Servlet、Filter或Listener
for (AnnotationEntry ae : annotationsEntries) {
String type = ae.getAnnotationType();
if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
processAnnotationWebServlet(className, ae, fragment);
}else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
processAnnotationWebFilter(className, ae, fragment);
}else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
fragment.addListener(className);
} else {
// Unknown annotation - ignore
}
}
}
}
设置启动项(是否项目启动实例化和初始化)
和初始化参数(添加<init-param>参数到wrapper也就是以后的Servelt)
StandardWrapper
添加到context的子容器集合中,并进行初始化和启动
// ContextConfig类方法
private void configureContext(WebXml webxml) {
// 添加web.xml中的监听器
for (String listener : webxml.getListeners()) {
context.addApplicationListener(listener);
}
// 其余代码
for (ServletDef servlet : webxml.getServlets().values()) {
// 获取web.xml中配置的servlet
Wrapper wrapper = context.createWrapper();
// 启动项设置
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
wrapper.setName(servlet.getServletName());
// 配置servlet初始化参数
Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
// 设置Servlet的权限定类名,以后会通过它反射获取空的Servelt对象
wrapper.setServletClass(servlet.getServletClass());
// 添加为context的子容器
context.addChild(wrapper);
}
for (Entry<String, String> entry :
webxml.getServletMappings().entrySet()) {
context.addServletMappingDecoded(entry.getKey(), entry.getValue());
}
// 其余代码
}
// StandardContext类方法
@Override
public Wrapper createWrapper() {
Wrapper wrapper = null;
if (wrapperClass != null) {
try {
wrapper = (Wrapper) wrapperClass.getConstructor().newInstance();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("createWrapper", t);
return null;
}
} else {
wrapper = new StandardWrapper();
}
...
return wrapper;
}
ApplicationContext
对象// StandardContext类方法
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return (context.getFacade());
}
ApplicationContextFacade
门面模式
,对外不暴露ApplicationContext,在ApplicationContextFacade里设置对外提供的方法属性,实际调用的ApplicationContext的方法属性// ApplicationContext类方法
private final ServletContext facade = new ApplicationContextFacade(this);
protected ServletContext getFacade() {
return (this.facade);
}
// ApplicationContextFacade类
public class ApplicationContextFacade implements
org.apache.catalina.servlet4preview.ServletContext {
private final ApplicationContext context;
}
public boolean listenerStart() {
// 其余代码
// 遍历所有监听器找出ServletContextListener类别的
for (Object instance : instances) {
if (!(instance instanceof ServletContextListener)) {
continue;
}
ServletContextListener listener = (ServletContextListener) instance;
try {
fireContainerEvent("beforeContextInitialized", listener);
// 调用其contextInitialized方法
if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
}
// 其余代码
}
}
ServletContextListener
接口,并添加@WebListener
注解,就是一个监听器public interface ServletContextListener extends EventListener {
// ServletContext创建时调用(context启动时)
public void contextInitialized(ServletContextEvent sce);
// ServletContext销毁时调用
public void contextDestroyed(ServletContextEvent sce);
}
实例化和初始化Filter
// StandardContext类方法
private HashMap<String, FilterDef> filterDefs = new HashMap<>();
private HashMap<String, ApplicationFilterConfig> filterConfigs =
new HashMap<>();
public boolean filterStart() {
...
for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
String name = entry.getKey();
try {
ApplicationFilterConfig filterConfig =
new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
...
}
}
...
}
// ApplicationFilterConfig类
ApplicationFilterConfig(Context context, FilterDef filterDef)
throws xxxException {
super();
this.context = context;
this.filterDef = filterDef;
// Allocate a new filter instance if necessary
if (filterDef.getFilter() == null) {
getFilter();
} else {
this.filter = filterDef.getFilter();
getInstanceManager().newInstance(filter);
initFilter();
}
}
Filter getFilter() throws xxxException {
...
// 实例化Filter
String filterClass = filterDef.getFilterClass();
this.filter = (Filter) getInstanceManager().newInstance(filterClass);
// 调用Filter的init方法
initFilter();
return (this.filter);
}
在web.xml中我们对于需要启动时加载的servlet会配置<load-on-startup>1</load-on-startup>
,这个功能的实现也是在StandardContext#startInternal中完成的。
public boolean loadOnStartup(Container children[]) {
// 获取所有loadOnStartup>0的Wrapper放入数字为key的map,这样数字越小越先被实例化
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
for (Container child : children) {
Wrapper wrapper = (Wrapper) child;
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0) {
continue;
}
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(wrapper);
}
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
wrapper.load();
}
}
}
return true;
}
loadOnStartup>=0
的Wrapper放入数字为key的map,这样数字越小越先被实例化
@Override
protected synchronized void stopInternal() throws LifecycleException {
// 给正在进行的异步请求一个完成的机会
long limit = System.currentTimeMillis() + unloadDelay;
while (inProgressAsyncCount.get() > 0 && System.currentTimeMillis() < limit) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
log.info(sm.getString("standardContext.stop.asyncWaitInterrupted"), e);
break;
}
}
// 将状态设置为 STOPPING 后,上下文将报告自身不可用,并且任何正在进行的异步请求都将超时
setState(LifecycleState.STOPPING);
// 调用子容器的stop方法
final Container[] children = findChildren();
for (int i = 0; i < children.length; i++) {
children[i].stop();
}
// 调用filter的销毁方法destroy
filterStop();
// 调用监听器的销毁方法contextDestroyed
listenerStop();
...
}
核心内容调用父类ContainerBase的destroyInternal方法。
StandardWrapper是在StandardContext的监听器ContextConfig里解析web.xml或@WebServlet注解类内容创建而来。最后再根据StandardWrapper内容创建Servlet。 StandardWrapper没有重写initInternal方法,而startInternal方法核心内容也是调用父类方法,接着上面实例化loadOnStartup>0的Servlet并初始化内容,查看load()方法。
// StandardWrapper类方法
public synchronized void load() throws ServletException {
// 创建当前Servlet对象,并初始化
instance = loadServlet();
// 其余代码
}
反射获取对象,并强转为Servlet类型
Servelt必须继承HttpServelt
,否则强转Servelt报错StandardWrapperFacade
门面模式
ServletConfig接口实现类StandardWrapper
包装一下// StandardWrapper类方法
public synchronized Servlet loadServlet() throws ServletException {
...
Servlet servlet;
try {
...
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
// servletClass是创建StandardWrapper时候,set进来的
// 反射获取Servelt对象
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
...
}
...
// 调用servlet的init方法
initServlet(servlet);
} finally {
...
}
return servlet;
}
// StandardWrapper类属性,创建StandardWrapper时,就赋值了此属性
protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
// 初始化方法
private synchronized void initServlet(Servlet servlet)
throws ServletException {
try {
if( Globals.IS_SECURITY_ENABLED) {
...
} else {
// 传入StandardWrapperFacade调用初始化方法
servlet.init(facade);
}
} catch (UnavailableException f) {
...
}
}
// 门面模式,将StandardWrapper放入StandardWrapperFacade对象里
// 对外只提供ServletConfig接口相关方法
public final class StandardWrapperFacade implements ServletConfig {
private final ServletConfig config;
public StandardWrapperFacade(StandardWrapper config) {
super();
this.config = config;
}
...
}
继承HttpServlet
类,类图如下GenericServlet抽象类的init方法
,然后将StandardWrapperFacade传递进来赋值configServletConfig
实际时StandardWrapperFacade类中的StandardWrapper
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
private transient ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
// 空实现,我们自定义的Servelt可以去覆盖无参init方法
this.init();
}
public void init() throws ServletException {
// NOOP by default
}
}
说起Mapper组件需要回到StandardService
的启动方法startInternal。顶级容器engine启动,使用线程池异步调用多个子容器StandardHost的start方法,host的启动,调用多个子容器StandardContext的start方法(包括监听器、过滤器、Servlet的实例化和初始化)。可以说顶级容器engine的启动所有的子容器都将启动。接下来需要组装Mapper组件(请求url和Servlet映射)。
protected final MapperListener mapperListener = new MapperListener(this);
// StandardService类方法
@Override
protected void startInternal() throws LifecycleException {
...
// 顶级容器engine启动
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
...
// 启动mapperListener,则时组装Mapper
mapperListener.start();
...
}
// MapperListener类方法
private void registerContext(Context context) {
...
List<WrapperMappingInfo> wrappers = new ArrayList<>();
for (Container container : context.findChildren()) {
prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerWrapper",
container.getName(), contextPath, service));
}
}
mapper.addContextVersion(host.getName(), host, contextPath,
context.getWebappVersion(), context, welcomeFiles, resources,
wrappers);
...
}
prepareWrapperMappingInfo用于准备注册到mapper下的wrapper,这儿mapper对于wrapper的支持是wrapper的包装对象WrapperMappingInfo。而一个context可能有多个wrapper
,所以WrapperMappingInfo是一个list。
简单来说就是将映射url
、wrapper名字
和资源只读标记等信息组合成对象添加到wrappers中。
private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) {
String wrapperName = wrapper.getName();
boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
String[] mappings = wrapper.findMappings();
for (String mapping : mappings) {
boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*"));
wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
}
}
最后addContextVersion方法又各种转化嵌套,对于我们自己定义的Servlet的Mapper映射对象的位置如下。
每个Service都有一个Mapper,如此看来,Mapper对象则记录了所有应用项目下的MappedWrapper(请求映射和Servelt对应的Wrapper),这样以后拿着请求mapping映射即可从Mapper中找到对应的Servelt。
至此,整个容器的启动过程就介绍完了,可以看到整个流程是由Server起步直到Wrapper结束。 其中Server代表的是整个tomcat应用,Service代表的是server.xml中的service节点。而后续的Engine与Host都是service中的子节点。 Context代表了webapps下的每个应用,子容器Wrapper表示web应用中的每个servlet。Context中的start方法中会创建当前web应用的ServletContext,实例化ServletContextListener监听器并执行,实例化Filter并调用初始化方法,实例化需要启动时加载的Servlet(loadOnStartup>0)并调用初始化方法。 最后容器启动后,组装每个应用下请求url和Servlet映射Mapper组件,后续请求时候需要。