tomcat6启动源码浅析二

本文详细剖析Tomcat启动过程及web应用部署机制,重点介绍了StandardEngine、StandardHost与StandardContext等容器类如何协作完成部署。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章只是很简单的跟踪了tomcat启动的主线(因为刚刚看代码,所以不可能一下子全部都看的懂,比较菜),本次把最近看到的一些新的内容和领悟继续分享一下,本次关心的主题是tomcat是怎么启动的时候部署webapp下面的每一个web应用的?
前面的启动分析在之前的文章中有介绍,直接从Catalina开始看起。在此先说明一点,即将出场的:StandardEngine,StandardHost,StandardContext都是继承自ContainerBase的容器类。
1. Catalina的load()方法会调用Digester的parse()解析相关的xml文件并且根据不同的Rule映射对应的Bean,其中映射到的StandardEngine实例会根据相应的Rule反射调用其父类addChild(Container child)把StandardHost加入到children的map中,而映射到的StandardService实例会根据相应的Rule反射调用其setContainer(Container container)把StandardEngine赋值给StandService的container属性,这样完成了热身运动。
2. 接着就是Catalina(start)-->StandardServer(start)-->StandardService(start)-->StandardEngine(start),到达此处还没有开始任何部署的主要工作,此时进入父类StandardEngine[ContainerBase].start()方法:
部分代码:
  ...
// Start our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).start();
}
...

这里的children[]数组里面就是之前的StandardHost类的实例,代码的走向: StandardEngine[name=Catalina,method=start]--> StandardHost[name=localhost,method=start],好戏就要开始了...
3. 继续之前,先说一下这部分的设计思路。基本上就是Observer模式主导,容器类通过addLifecycleListener方法调用事件发布辅助类LifecycleSupport的addLifecycleListener方法注册listener,同时LifecycleSupport提供方法fireLifecycleEvent给各个已注册的listener发送事件通知,主要是给*Config的类,例如:EngineConfig,HostConfig,ContextConfig等等。通过这些listener的lifecycleEvent方法响应事件。
言归正传,进入StandardHost的start方法首先要先执行init方法初始化,在这个过程中:
部分代码:
 ...
HostConfig deployer = new HostConfig();
addLifecycleListener(deployer);
...

注册了一个关键的listener,初始化完毕后,继续执行start进入容器父类ContainerBase的start方法使用其LifecycleSupport实例变量发送通知lifecycle.fireLifecycleEvent(START_EVENT, null);此时的listener实例HostConfig接收消息进行处理执行到if(event.getType().equals(Lifecycle.START_EVENT)) start();进入其自身的start()方法继续执行,直到if (host.getDeployOnStartup()) deployApps();进入deployApps方法:
 protected void deployApps() {

File appBase = appBase();
File configBase = configBase();
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs, and loop if additional descriptors are found
deployWARs(appBase, appBase.list());
// Deploy expanded folders
deployDirectories(appBase, appBase.list());

}

所有要部署的web应用都是通过这个方法进行部署。下面以我的webapp为例(包含host-manager应用)进行说明。
4. 首先是deployDescriptors(File configBase, String[] files)其中files里面包含了host-manager.xml,该文件就是位于/conf/Catalina/localhost/下,接着就是在以files长度循环内调用deployDescriptor(String contextPath, File contextXml, String file)分别的部署各个应用。此方法有如下代码片断:
   ...
if (context instanceof Lifecycle) {
Class clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.newInstance();
((Lifecycle) context).addLifecycleListener(listener);
}
...
host.addChild(context);
...

这里的context对应web应用的StandardContext,host当然就是前两级容器StandardEngine[Catalina].StandardHost[localhost],而listener就是对应的ContextConfig。当执行host.addChild(context);时会跑到StandardHost的父类ContainerBase的addChild(Container child)-->addChildInternal(Container child),在这个方法里,作为child的StandardContext[/host-manager]除了被添加成三级子容器之外,调用了它的start方法。
5. 在StandardContext start里,lifecycle.fireLifecycleEvent(START_EVENT, null);又出现了!只好进入其对应的listener实例ContextConfig。它的事件处理也是类似的if (event.getType().equals(Lifecycle.START_EVENT)) start();只好再进入其start方法:
...
// Process the default and application web.xml files
defaultWebConfig();
applicationWebConfig();
...

在这个方法里处理了webapp下host-manager应用的web.xml文件等等。完毕之后现在的容器就成了StandardEngine[Catalina].StandardHost[localhost].StandardContext[/host-manager],然后有多少应用就生成多少三级容器。
6. 其他的web应用的部署方式类似。每一个最终的容器Context,都有一个StandardWrapper,它本身也是一个容器但是不允许再有子容器了。本例对应的就是StandardWrapper[host-manager],这个东东就是负责对每个servlet的生命周期进行管理,同时管理其请求响应等等。
至此,我想关于tomcat的启动的分析应该可以算入门了。其实看源码具体的类的实现的代码不见得处处放光,很多都是一般般的实现。但是每个模块的设计,解藕和衔接是我一直想追求的技能,简简单单的代码组合在一起就成为一个变形金刚。希望能从中得到更多的启发,分享更多。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值