首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Tomcat 5.5.26源代码分析——起动过程(二)

2013-10-08 
Tomcat 5.5.26源代码分析——启动过程(二)Tomcat每个运行实例需要使用自己的conf、logs、temp、webapps、work和s

Tomcat 5.5.26源代码分析——启动过程(二)

Tomcat每个运行实例需要使用自己的conf、logs、temp、webapps、work和shared目录,因此CATALINA_BASE就指向这些目录。 而其他目录主要包括了Tomcat的二进制文件和脚本,CATALINA_HOME就指向这些目录。

如果我们希望再运行另一个Tomcat实例,那么我们可以建立一个目录,把conf、logs、temp、webapps、work和shared拷贝到该目录下,然后让CATALINA_BASE指向该目录即可。

下面,我们看看Bootstrap是如何设置CATALINA_HOME和CATALINA_BASE。

?
我们可以通过修改catalina.bat中CATALINA_HOME和CATALINA_BASE的值,来设置catalina.home和catalina.base这两个系统变量。
初始化类加载器体系顾名思义,initClassLoaders() 负责初始化类加载器。看代码之前,我们先看看Tomcat的类加载器体系。
Tomcat的类加载器体系各种组件容器(Tomcat、JBoss、GlassFish、Geronimo等),都会有自己的类加载器体系。这主要是为了要把开发者编写的各种组件应用(WAR、EAR等)部署到容器中,并实现组件应用之间的隔离。

Tomcat也实现了自己的类加载器体系。这个在Tomcat的官方文档中有详细介绍,详见http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html。这里做点简单介绍。

Tomcat的类加载器体系如下图所示:

???? Bootstrap
???? ? ? |
?????? System
???????? |
?????? Common
??? ? ? / \
??? Catalina Shared
? ? ? ? ?? ?? / \
??????? Webapp1 Webapp2 ...

BootStrap就是JVM的启动类加载器,负责加载Java核心类库和系统扩展类库(%JAVA_HOME%/jre/lib/ext下的jar文件)。有的JVM实现提供了两个类加载器,分别加载核心类库和系统扩展类库。我们这里仍用一个Bootstrap类加载器表示,不影响理解。

System 是JVM的系统类加载器,负责加载CLASSPATH下的jar文件,这些文件包括:
  1. %CATALINA_HOME%/bin/bootstrap.jar
  2. %JAVA_HOME%/lib/tools.jar
  3. %CATALINA_HOME%/bin/commons-logging-api-x.y.z.jar
  4. %CATALINA_HOME%/bin/tomcat-juli.jar
  5. %CATALINA_HOME%/bin/tomcat-daemon.jar%CATALINA_HOME%/bin/jmx.jar(即之前提到的JDK 1.4兼容包)
其中,1和2是在catalina.bat中指定的,3-6是在bootstrap.jar的META-INF/MANIFEST.MF文件中指定的。

Common 是公共类加载器,负责加载Tomcat内部和Web应用程序都可以看到的类。%CATALINA_HOME%/conf/catalina.properties文件中指定了这些类:

common.loader=${catalina.home}/common/classes,${catalina.home}/common/i18n/*.jar,${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar

可见,Common加载的是%CATALINA_HOME%/common目录下的jar文件。

Catalina负责加载Tomcat内部使用的类,这些类对于Web应用程序不可见。同样,%CATALINA_HOME%/conf/catalina.properties文件中指定了这些类:

server.loader=${catalina.home}/server/classes,${catalina.home}/server/lib/*.jar

可见,Catalina加载的是%CATALINA_HOME%/server目录下的jar文件。

Shared负责加载在Web应用程序之间共享的类,这些类对于Tomcat内部是不可见的。同样,%CATALINA_HOME%/conf/catalina.properties文件中指定了这些类:

shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar

可见,Catalina加载的是%CATALINA_BASE%/shared目录下的jar文件。注意,这里是CATALINA_BASE。shared属于Tomcat的工作目录。

Webapp负责加载Web应用程序的类,这些类只对本Web应用程序可见。很显然,每个Web应用程序都有一个独立的Webapp类加载器。它们加载的类包括WAR包中/WEB-INF/classes和/WEB-INF/lib目录下的类。

需要注意的是,Webapp并没有遵循类加载器委派模型。Webapp优先从自己的搜索路径中加载类,而不是委派给父亲Shared。这样做的原因应该是保证Web应用程序优先使用自己的类。但是,System负责加载的类不应该被覆盖,因此,Webapp会首先委派给System,然后自己加载,接着才会委派给父亲Shared。这个可以参考org.apache.catalina.loader.WebappClassLoader的loadClass方法。

?

PS:Tomcat 7的类加载体系有所简化,如下图所示:

???? Bootstrap
???? ? ? |
?????? System
???????? |
?????? Common
??? ? ? / \
? Webapp1 Webapp2 ...

?

在这种体系下,Tomcat内部实现类对Web应用是可见的。如果担心安全风险,可以以-security方式启动Tomcat。

?

创建类加载器,必须要知道类搜索路径是什么。前面提到,Tomcat各类加载器的类搜索路径都定义在%CATALINA_HOME%/conf/catalina.properties中。该配置文件的主要内容如下:

?

可以看出,Catalina的load方法有两个版本:有参数和无参数。

有参数版本,首先调用了arguments方法来处理参数。如果处理成功,则再调用无参数版本。因此,load的核心逻辑在无参数版本中。

?

Catalina加载的主要流程参见上述代码中的中文注释,应该比较清楚,这里就不一一赘述了。

需要注意的是Digester,它是Apache基金会的另一个项目,主要负责解析XML并执行一定的操作。其主要原理是,为XML的每个元素配置特定的规则,规则描述了Digester遇到该元素时需要执行的操作。

Tomcat使用Digester来解析配置文件(默认是conf/server.xml),并根据配置创建各种组件,并建立组件之间的关联。创建和关联,都是通过自定义的规则来实现的。我们以conf/server.xml的部分内容和部分规则为例,解释一下Digester的原理。

配置文件的部分内容:

?


其中,每个XML元素代表一个组件对象,元素中属性对应了组件的成员变量。例如,<Server>就代表Server组件,该组件对象有两个成员变量port和shutdown。不难猜到,它们其实是之前提到的SHUTDOWN端口和SHUTDOWN命令。

Server组件包含了Listener对象和Service组件。Service组件又包含了Connetor组件和Engine组件。Engine组件又包含了Host组件。Host组件也包含了...这就是组件之间的关联。

至于这些组件的作用是什么,关联关系为什么是这样的,我们会在后面的文章中看到。

下面我们在看看Digester规则。规则以方法调用的定义。Tomcat启动相关的规则定义在createStartDigester方法中,部分代码如下:

?

start方法执行之前,需要确保load方法已经执行。如果load方法已经执行,那么成员变量server肯定被赋值。因此,start方法首先判断成员变量server是否为null,如果是,则调用load方法。

接着,调用server的start方法,启动Catalina。启动阶段的工作和加载阶段还是不同的。例如,Connector组件负责网络通信,加载阶段只是创建组件对象,启动阶段才会监听端口。

然后,Catalina的主线程会进入await状态,如果成员变量await被设置成true的话。《Tomcat 5.5.26源代码分析——启动过程(一)》中也提到,如果await被设置成true,那么Tomcat的主线程将监听SHUTDOWN端口,等待SHUTDOWN命令,从而我们可以在Tomcat进程外部通过网络停止Tomcat的运行。Tomcat收到SHUTDOWN命令之后,主线程就会退出await状态,await方法也执行结束,从而stop方法被调用,Tomcat停止运行。

这里我们需要注意一下server instanceof Lifecycle的代码。Lifecycle是一个生命周期接口,定义了各种生命周期方法start和stop。只有实现了该接口的组件才能拥有生命周期方法。生命周期方法的调用是嵌套的,父组件的生命周期方法会调用子组件的同名方法。这样,只需调用顶层组件的start方法,就可以启动所有子孙组件。stop也一样。因此,生命周期方法和组件关联关系,使得Tomcat很容易管理各个组件的启动和停止。

OK,start方法也介绍完了。各组件的start方法被调用之后,Tomcat已经处于就绪状态,等待请求的到来。

本文的最后,详细讨论一下await状态的实现细节。

await状态setAwait方法

Bootstrap类的main方法中,处理start启动参数时,会调用setAwait方法。

 public void await() {        // port是SHUTDOWN端口。如果值为-2,则表示不进入await状态,直接返回;如果值为-1,则表示通过简单循环的方式来实现await状态。        // 如果port为-2,则表示不进入await状态,直接返回;如果值为-1,则表示通过简单循环的方式来实现 await状态。        // 如果port为-1,则表示通过简单循环的方式来实现 await状态。此方法适合嵌入式Tomcat。        // 如果port为其他值,则表示通过监听网络端口的方式来实现 await状态。        // Negative values - don't wait on port - tomcat is embedded or we just don't like ports        if( port == -2 ) {            // undocumented yet - for embedding apps that are around, alive.            return;        }        if( port==-1 ) {            while( true ) {                try {                    Thread.sleep( 100000 );                } catch( InterruptedException ex ) {                }
1 楼 lkjust08 2010-03-27   lz写的很详细,可能是小弟知识不足,具体的内容还是看不懂,看来,小弟还是要加强学习呀。 2 楼 sw1982 2010-03-27   分析的很精辟啊,而且你推荐的那本书确实牛,看了一节就很大收益 3 楼 jackdown 2010-03-31   分析的很详细啊,感谢,看来我得照着5.5看看,本地是6.0很多不对了。
另:楼上说的是哪本书啊,分享下啊。 4 楼 jarfield 2010-03-31   jackdown 写道分析的很详细啊,感谢,看来我得照着5.5看看,本地是6.0很多不对了。
另:楼上说的是哪本书啊,分享下啊。

《How Tomcat Works》,你可以上百度文库搜一下,里面有的。 5 楼 jackdown 2010-04-06   jarfield 写道jackdown 写道分析的很详细啊,感谢,看来我得照着5.5看看,本地是6.0很多不对了。
另:楼上说的是哪本书啊,分享下啊。

《How Tomcat Works》,你可以上百度文库搜一下,里面有的。

嗯,看到了,感觉不错,谢谢。 6 楼 jakehuu 2011-02-24   佩服楼主 现在正在看 《how tomcat works》 ,楼主能够提供这本书里面的例子程序啊?我的邮箱 huqiao86@gmail.com

热点排行