Tomcat-Container源码分析
Tomcat源码分析—-Container初始化与加载 https://yq.aliyun.com/articles/20172
Digester分析:http://blog.csdn.net/wgw335363240/article/details/5869660
《看透SpringMVC源码分析与实践》
在TOMCAT启动-源码跟踪的Bootstrap.load() 方法中,调用了createStartDigester();
该方法通过解析conf/server.xml
,创建Server,初始化Connector和Container,但是并未深究,在这里就研究下createStartDigester()中到底做了些什么。
Server.xml
首先来查看下conf/server.xml的内容:
<Server port="8005" shutdown="SHUTDOWN">
<!-- listeners , GlobalNamingResources 省略-->
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<!-- defaultHost:默认域名 (url匹配优先级最低) -->
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<!--
name: 表示站点域名 (url匹配优先级最高)
appBase: 指定站点的位置
unpackWARs: 是否自动解压war文件
autoDeploy:是否自动部署,若为true,在tomcat运行过程中,在webapps目录下增加了应用将会自动部署。
-->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- 域名别名 (url匹配优先级第二高) -->
<alias>xxx</alias>
<!-- 配置context.xml -->
<Context path="/img" docBase="E:/data/ftpFiles/" debug="0"></Context>
<Context path="/zz" docBase="/home/zzz.war" debug="0">
</Host>
</Engine>
</Service>
</Server>
在这个xml文件中,Server下包含了Service,Service下包含了Connector和Engine,Engin下包含了Host。是时候放出一张图了。
Container组成
Container容器的结构图
下图是一个非常常见的Container容器的结构图。
- Engine 表示一个Servlet引擎,它可以包含一个或多个子容器,比如多个Host或者Context容器;
- Host 表示一台虚拟的主机,它可以包含一系列Context容器;
- Context 表示一个唯一的ServletContext,一个 Context 对应一个 Web 工程,它可以包含一个
或多个Wrapper容器;
Context配置方式
- 1.文件配置
- 将<context/>配置添加到conf/server.xml
- conf/[enginename]/[hostname]/应用名.xml
- 应用自身目录/META-INF/context.xml
- conf/context.xml文件 ——
整个tomcat共享
- conf/[enginename]/[hostname]/context.xml.default 文件 —-
整个站点(host)共享
- 2.将war放置到Host配置的appBase目录下
- 3.将应用文件夹放置到Host配置的appBase目录下
- Wrapper 表示一个独立的Servlet定义,即Wrapper本质就是对Servlet进行了一层包装。
Container的类图
Container是Tomcat中容器的接口.Container有四个子接口Engine,Host,Context,Wrapper和一个默认abstract实现类ContainerBase
。这四个子接口都有一个对应的StandradXXX
实现类。
Container实例化
Bootstrap.load() 执行过程:Bootstrap.load() --> catalina.load() --> createStartDigester() --> getServer().init() --> 遍历services[i].init() --> container.init() 和 遍历connectors[i].init();
- 通过createStartDigester()方法创建实例化了Server,Container和Connector等相关组件。
- container.init() 进行初始化
createStartDigester()创建
//org.apache.catalina.startup.Catalina
protected Digester createStartDigester() {
//省略.....
//当解析xml文件时,遇到"Server"就初始化一个"org.apache.catalina.core.StandardServer”对象,并且把该对象压入栈顶
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//从server.xml读取"Server"元素的所有{属性:值}配对,用对应的Setter方法将属性值设置到堆栈顶层元素(Server)。
digester.addSetProperties("Server");
//遇到"Server"结束符时,调用"次顶层元素(Catalina)"的"setServer"方法,
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
/*
* 调用Rule的begin 、body、end、finish方法来解析xml,入栈和出栈给对象赋值
* ConnectorCreateRule是通过server.xml中配置的Connector的protocol类型,寻找对应的className
*/
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
//判断connector是否使用线程池,即是否配置executor属性
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
//调用RuleSetBase.addRuleInstances来解析xml标签,可以RuleSetBase认为是一组Rule。
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
/*
* 当发现配置了engine,设置它的parentClassLoader为parentClassLoader
* parentClassLoader = Catalina.class.getClassLoader();
*/
digester.addRule("Server/Service/Engine",new SetParentClassLoaderRule(parentClassLoader));
return (digester);
}
Container启动
Container的启动时通过init和start
方法来完成的,这两个方法是在Tomcat启动过程时被Service调用。
Container的init和start方法也会调用initInternal和startInternal
来具体处理,但是container和之前的tomcat整体结构启动过程稍微有点不一样,不同的是:
- Container四个子容器有一个共同的父类
ContainerBase
,它定义了通用的initInternal和startInternal
处理内容。 - 除了最顶层容器(Engine)的init是被Service调用的,子容器(Host,Context,Wrapper)的init方法并不是此时在容器中循环调用的,而是在执行start方法时通过状态判断调用的。
- start方法除了在父容器的startInternal方法中调用,还会在父容器的添加子容器的addChild方法中调用,这主要因为
Context和Wrapper是动态添加的
,在web.xml配置一个Servlet就可以添加一个Wrapper,所以Context和Wrapper是在容器启动的过程中cai动态查找出来添加到响应的父容器中的。
Container-init
container.init()
container.init()方法调用的入口是StandardService.initInternal()
中调用container.init(),
//org.apache.catalina.core.StandardService
protected void initInternal() throws LifecycleException {
if (container != null) {
container.init();
}
//省略其他代码.......
}
此时container的name="Catalina"的StandardEngine类型的容器
,它有一个name="localhost"的StandardHost
的子容器。
这些都是在上文createStartDigester()方法中创建实例化的。
init()执行轨迹
如下图:
ContainerBase.initInternal()
ContainerBase.initInternal()方法主要初始化了ThreadPoolExecutor类型的属性startStopExecutor,用于管理启动和关闭的线程。
//org.apache.catalina.core.ContainerBase
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue =
new LinkedBlockingQueue<Runnable>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
这个方法并没有设置生命周期的相应状态,所以对于的具体容器也没有设置相应的状态,那么即使init方法进行了初始化,在start启动前也会再次调用init方法。
Container-start
container.start()
container.start()方法调用的入口是StandardService.startInternal()
中调用container.start(),
start()执行轨迹
如下图:
ContainerBase.startInternal()
ContainerBase.startInternal()方法主要完成:
- 如果有Cluster和Realm则调用其start方法。
Cluster用于配置集群,用来同步Session;Realm是tomcat的安全域,可以用来管理资源的访问权限。
- 调用所有子容器的start方法启动
- 调用管道中Value的Start方法来启动管道
- 启动完成后将生命周期状态设置为STARTING状态。
- 启动后台线程处理一些事情:如session过期清理,jsp动态替换,动态部署资源包等。
//org.apache.catalina.core.ContainerBase
@Override
protected synchronized void startInternal() throws LifecycleException {
//启动loader,manager,cluster,realm,resources 略.....
//获取所有子容器
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
//调用执行StartChild.call() --> 内部调用 chilid[i].start()方法
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
//处理子容器启动线程的结果results
boolean fail = false;
for (Future<Void> result : results) {
result.get();//获取结果,阻塞..
}
//如果有子容器启动失败,抛出异常 省略.....
//启动管道
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
setState(LifecycleState.STARTING);
//启动后台线程,定期调用容器的backgroundProcess()方法。
threadStart();
}
StartChild
是一个实现了Callable的内部类,主要作用就是调用子容器的child.start();
方法。
子容器start()执行完成之后,接着会启动容器的管道。tomcat-Pipeline-Value管道,管道的启动也是直接调用start方法来完成的。管道启动完成之后设置生命周期状态,最后调用threadStart方法来启动后台线程。
threadStart()
threadStart()方法设置一个thread为后台线程
,并调用ContainerBackgroundProcessor()的run方法。
//org.apache.catalina.core.ContainerBase
protected void threadStart() {
//省略部分判断代码...
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true); //设置后台进程
thread.start();
}
ContainerBackgroundProcessor
//org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor
protected class ContainerBackgroundProcessor implements Runnable {
@Override
public void run() {
while (!threadDone) {
//每隔backgroundProcessorDelay定期执行本方法
Thread.sleep(backgroundProcessorDelay * 1000L);
if (!threadDone) {
Container parent = (Container) getMappingObject();
ClassLoader cl =
Thread.currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
processChildren(parent, cl);
}
}
}
protected void processChildren(Container container, ClassLoader cl) {
//执行backgroundProcess
container.backgroundProcess();
//递归执行子容器的processChildren --> backgroundProcess
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
}
}
上述代码的作用是每该线程每10s会调用一次processChildren,最终执行container.backgroundProcess()方法,ContainerBase中的默认backgroundProcess实现如下:
container.backgroundProcess()
-
ContainerBase.backgroundProcess()
:调用一系列组件的backgroundProcess方法。值得关注的是:loader.backgroundProcess();,通过此方法达到**重新加载**webapps,以实现热部署
。
//org.apache.catalina.core.ContainerBase
public void backgroundProcess() {
if (!getState().isAvailable())
return;
cluster.backgroundProcess(); //SimpleTcpCluster:集群心跳测试
loader.backgroundProcess();//WebappLoader.backgroundProcess()----reload()重新加载
manager.backgroundProcess();//ManagerBase:废弃所有过期的session
Realm realm = getRealmInternal();
realm.backgroundProcess(); //默认UserDatabaseRealm,do nothing.
Valve current = pipeline.getFirst();
while (current != null) {
current.backgroundProcess();
current = current.getNext();
}
//触发PERIODIC_EVENT事件
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
}
WebappLoader.backgroundProcess()--实现热部署
//org.apache.catalina.loader.WebappLoader
public void backgroundProcess() {
//reloadable即为是否开启热部署,modified()则是当前文件是否有修改的判断。
//`通过<Context reloadable="true"></Context> 配置。`
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader
(WebappLoader.class.getClassLoader());
if (container instanceof StandardContext) {
((StandardContext) container).reload();
}
} finally {
if (container.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(container.getLoader().getClassLoader());
}
}
} else {
closeJARs(false);
}
}
-
StandardWrapper.backgroundProcess()
:方法首先调用super.backgroundProcess()
方法,然后再执行Servlet.periodicEvent()
来进行
//org.apache.catalina.core.StandardWrapper
public void backgroundProcess() {
super.backgroundProcess();//调用ContainerBase.backgroundProcess()
if (!getState().isAvailable())
return;
if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) {
//调用Servlet.periodicEvent,实际上调用JspServlet.periodicEvent()
((PeriodicEventListener) getServlet()).periodicEvent();
}
}
//org.apache.jasper.servlet.JspServlet
public class JspServlet extends HttpServlet implements PeriodicEventListener {
//省略其他代码.....
private transient JspRuntimeContext rctxt;
@Override
public void periodicEvent() {
//检测未加载的jsp
rctxt.checkUnload();
//检测未编译的jsp
rctxt.checkCompile();
}
}
Container容器组件启动链
容器启动的启动节点是在Service调用了,container.start()
,然后会逐层的启动: StandardEngine --> StandardHost --> StandradContext --> StandardWrapper
,如下图:
Engine
Engine的默认实现是StandradEngine
,它默认实现了initInternal和startInternal,代码如下:
//org.apache.catalina.core.StandardEngine
public class StandardEngine extends ContainerBase implements Engine {
@Override
protected void initInternal() throws LifecycleException {
getRealm();
super.initInternal();
}
@Override
protected synchronized void startInternal() throws LifecycleException {
//直接调用父类的同名方法
super.startInternal();
}
@Override
public Realm getRealm() {
//如果没有配置realm,使用一个默认的NullRealm
Realm configured = super.getRealm();
if (configured == null) {
configured = new NullRealm();
this.setRealm(configured);
}
return configured;
}
}
Host
Host的默认实现类是StandradHost
,没有重写initInternal方法,默认调用父类的initInternal方法,代码如下:
//org.apache.catalina.core.StandardHost
public class StandardHost extends ContainerBase implements Host {
@Override
protected synchronized void startInternal() throws LifecycleException {
//errorValve = “org.apache.catalina.valves.ErrorReportValve”
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
//如果管道中没有指定的errorValve类,则假如管道
if(!found) {
Valve valve = (Valve) Class.forName(errorValve).newInstance();
getPipeline().addValve(valve);
}
}
//调用父类即ContainerBase.startInternal()
super.startInternal();
}
}
Host启动调用自身的startInternal()的方法之后,会调用父类ContainerBase的同名方法,在该方法中会执行setState(LifecycleState.STARTING);
,此主动触发Host的监听Listener的相关事件。
而在上文中,createStartDigester()创建 中,创建Host的过程中,给Host增加了监听类HostConfig
//1. org.apache.catalina.startup.Catalina.
protected Digester createStartDigester() {
//....省略其他
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
}
//2.org.apache.catalina.startup.HostRuleSet
public void addRuleInstances(Digester digester) {
//匹配xxx/Host 标签,创建org.apache.catalina.core.StandardHost类,如果存在className属性则创建该属性对应的类。
digester.addObjectCreate(prefix + "Host",
"org.apache.catalina.core.StandardHost",
"className");
digester.addSetProperties(prefix + "Host");
digester.addRule(prefix + "Host",
new CopyParentClassLoaderRule());
//使用LifecycleListenerRule规则,给当前Host添加Listener,类型为HostConfig
digester.addRule(prefix + "Host",
new LifecycleListenerRule
("org.apache.catalina.startup.HostConfig",
"hostConfigClass"));
//省略其他......
}
//3.org.apache.catalina.startup.LifecycleListenerRule
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
//peek()表示获取当前栈顶数据,但是不从栈中移除它。此时栈顶是Host
Container c = (Container) digester.peek();
//省略部分代码.....
//反射生成listener
Class<?> clazz = Class.forName(className);
LifecycleListener listener = (LifecycleListener) clazz.newInstance();
//给Host添加监听。
c.addLifecycleListener(listener);
}
到这里我们可以认为那么在接着StandardHost启动过程中,之后触发执行HostConfig相关的事件的流程,代码如下:
//1.org.apache.catalina.core.ContainerBase
protected synchronized void startInternal() throws LifecycleException {
//省略其他....
setState(LifecycleState.STARTING);
}
//2.org.apache.catalina.util.LifecycleBase
protected synchronized void setState(LifecycleState state)
throws LifecycleException {
setStateInternal(state, null, true);
}
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
//事件类型是:Lifecycle.START_EVENT即“start”
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
protected void fireLifecycleEvent(String type, Object data) {
lifecycle.fireLifecycleEvent(type, data);
}
//3.org.apache.catalina.util.LifecycleSupport
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
最终调用HostConfig.lifecycleEvent()
方法,最关键的逻辑:
- lifecycleEvent():判断事件类型,调用start()方法
- start()调用deployApps方法
- deployApps()通过三种方式部署应用。
- deployDescriptor()部署应用,并生成Context,且将Context添加监听ContextConfig,并将其添加至Host的Child列表中。
//org.apache.catalina.startup.HostConfig
public class HostConfig implements LifecycleListener {
public void lifecycleEvent(LifecycleEvent event) {
// Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
setCopyXML(((StandardHost) host).isCopyXML());
setDeployXML(((StandardHost) host).isDeployXML());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
setContextClass(((StandardHost) host).getContextClass());
}
} catch (ClassCastException e) {
log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
}
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check(); //监听PERIODIC_EVENT事件
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
start(); //监听start事件 ---
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
public void start() {
//省略其他....
//如果Host配置了 autoDeploy
if (host.getDeployOnStartup())
deployApps();
}
//在此可以看到:可以通过如下三种方式deployApps
protected void deployApps() {
File appBase = appBase();
File configBase = configBase();
String[] filteredAppPaths = filterAppPaths(appBase.list());
//configBase = ~/[ENGINE-NAME]/[HOST-NAME]
deployDescriptors(configBase, configBase.list());
//部署war
deployWARs(appBase, filteredAppPaths);
//部署文件夹
deployDirectories(appBase, filteredAppPaths);
}
//部署,生成Context并添加至host的child列表中
protected void deployDirectory(ContextName cn, File dir) {
//省略.....
//contextClass = "org.apache.catalina.core.StandardContext"
Context context = (Context) Class.forName(contextClass).newInstance();
//configClass = "org.apache.catalina.startup.ContextConfig"
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.newInstance();
//同理,给context添加listener监听ContextConfig
context.addLifecycleListener(listener);
//省略.....
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName());
host.addChild(context);
}
}
到这里为止,Host启动完毕,在它启动的同时伴随着Cntext的部署
,且Context创建过程中添加了ContextConfig监听,它会影响到后续的Wrapper的创建和启动。
Context
Context的默认实现类StandardContext默认实现了initInternal和startInternal,在startInternal方法主要做了如下操作:
- 创建WebappLoader,并启动
- 触发configure_start事件,ContextConfig做出相应,生成并启动子容器(Wrapper)
- 创建ServletContext、
StandardContext --> ApplicationContext --> ServletContext
并设置了ServletContext相关属性 - 调用了web.xml中定义的Listener以及初始化了其中的Filter和Load-on-startup的Servlet,
代码如下:
//org.apache.catalina.core.StandardContext
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter {
protected void initInternal() throws LifecycleException {
super.initInternal();
if (processTlds) {//是否允许解析tld:默认为true
//添加、解析 tld标签
this.addLifecycleListener(new TldConfig());
}
// Register the naming resources
if (namingResources != null) {
namingResources.init();
}
//发送通知,主要用具JMS
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.object.created",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
}
protected synchronized void startInternal() throws LifecycleException {
//设置resources,在webapploader创建webappclassloader时,需要指定此信息为加载目录
if (webappResources == null) {
if ((getDocBase() != null) && (getDocBase().endsWith(".war")) &&
(!(new File(getBasePath())).isDirectory()))
setResources(new WARDirContext());
else
setResources(new FileDirContext());
}
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
//设置loader
setLoader(webappLoader);
}
//getServletContext().setAttribute(xxx,xxx) 省略....
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());
//触发configure_start事件,ContextConfig监听了此事件
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
//启动子容器,Wrapper容器。
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
if (ok) {
//执行webappLoader.start() 启动
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
}
//listener启动,调用contextInitialized方法
if (ok) {
if (!listenerStart()) {
log.error( "Error listenerStart");
ok = false;
}
}
//filetr启动,调用init方法
if (ok) {
if (!filterStart()) {
log.error("Error filterStart");
ok = false;
}
}
// Load-on-startup的Servlet启动,调用init
if (ok) {
loadOnStartup(findChildren());
}
//启动父类的后台进程,定时执行backgroundProcess,监听变化
super.threadStart();
}
//StandardContext --> ApplicationContext --> ServletContext
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return (context.getFacade());
}
}
关于StandardContext,WebappLoader和WebappClassLoader的关系
:
- StandardContext启动时,创建了WebappLoader,并将其设为自身的loader属性,然后启动WebappLoader
- WebappLoader启动时,创建了WebappClassLoader,并设置了WebappClassLoader的
resources
等属性,然后启动 - WebappClassLoader启动,工作比较简单,仅设置了 encoding.
在Context启动时,执行了fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
,ContextConfig会监听到该事件,会调用configureStart()方法,加载配置servlet-wrapper
**,代码如下:
//org.apache.catalina.startup.ContextConfig
public class ContextConfig implements LifecycleListener {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();//CONFIGURE_START_EVENT触发configureStart()方法
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
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() {
//执行webConfig方法
webConfig();
//省略 一系列 角色,授权等校验.....
}
protected void webConfig() {
//省略其他......
WebXml webXml = createWebXml();
//1.如果jars中包含了web-fragment.xml文件,将他们合并到一起
Map<String,WebXml> fragments = processJarsForWebFragments();
// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
//省略其他.....
// Step 8. Convert explicitly mentioned jsps to servlets
if (ok) {
convertJsps(webXml);
}
// Step 9. 将合并后的web.xml应用到context
if (ok) {
webXml.configureContext(context);
}
//省略其他.....
}
}
重点关注Step 9
,它调用了webXml.configureContext(context)
,其内部逻辑是:
//org.apache.catalina.deploy.WebXml
public void configureContext(Context context) {
//context设置属性值,略......
//filters
for (FilterDef filter : filters.values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
for (FilterMap filterMap : filterMaps) {
context.addFilterMap(filterMap);
}
//listeners
for (String listener : listeners) {
context.addApplicationListener(
new ApplicationListener(listener, false));
}
for (ServletDef servlet : servlets.values()) {
Wrapper wrapper = context.createWrapper();
// servlet转换为 wrapper,略.....
wrapper.setOverridable(servlet.isOverridable());
//添加到child子容器中
context.addChild(wrapper);
}
//省略.........
}
webConfig主要完成如下操作:
- 解析合并web.xml,生成WebXml对象
- 通过Webxml对象的configureContext方法:生成servlet、filter、jsp2Servlt等信息,配置加载Wrapper,并将Wrapper添加至Context的子容器里面
- 完成其他的初始化操作…
自此可以看到Wrapper的生成过程,同样的他们也会在后台进程周期执行过程中启动
Wrapper
Wrapper的默认实现类StandardWrapper没有重写initInternal方法,它的stratInternal方法代码如下:
//org.apache.catalina.core.StandardWrapper
@Override
protected synchronized void startInternal() throws LifecycleException {
// broadcaster发送通知,省略.....
// 调用服了startInternal方法
super.startInternal();
//设置servlet有效
setAvailable(0L);
}
Wrapper没有XXXConfig样式的LifecycleListener监听器。
从Engine—Host—Contex—-Wrapper这个链路上的容器初始化已经完成。
上一篇: Tomcat容器入门介绍
下一篇: Apache Tomcat 介绍