tomcat 4.1.30启动过程的源码分析
tomcat 4.1.30启动过程的源码分析
前几天为了解决sinpool兄的《多线程的问题。》一帖,专门看了一下tomcat 4.1.30的源码,
其中重点研究了tomcat的启动这一部分,个人感觉tomcat的源码还是写的很清楚易懂,值得一看。
(以前看过struts的部分代码,感觉也比较经典)
然后我看后的代码整理了一下,附在下面,希望对其他人有用,也希望感兴趣的兄弟可以多看看好的代码,
肯定对自己的程序设计和代码质量颇有益处。
一. 启动类(包含main()方法的类):
org.apache.catalina.startup.Bootstrap
这个类是tomcat的启动类,主要按照如下步骤,进行主要的启动工作:
1. 创建3个ClassLoader:common,catalina和share,它们对应tomcat的3个Classloader,我想对tomcat
的classloader有研究的兄弟对这个肯定不陌生,其中common classloader是紧跟在系统的classloader(也就是
系统环境变量中设置的CLASSPATH所对应的classloader),而catalina classloader是common的子classloader,是tomcat
运行所需要的类的classloader,而shared classloader也是common的子classloader,是和catalina平级的classloader,
之所以说是shared classloader,是因为它是所有tomcat下面发布的webapp classloader(每一个web app都有一个自己的classloader)
的父classloader。它们这3个classloader分别读取tomcat home下面的common, server和shared三个目录里面的classes和lib目录,
用于初始化自己所控制的类库和资源。
2. 创建启动一个的org.apache.catalina.startup.Catalina类的实例,并调用它的process方法,这里使用的是java的reflection技术。
然后调用这个实例的process方法,并把Bootstrap接受到的命令行参数传递进去了,这里Bootstrap类并没有解析传给它的命令行参数。
当然在调用process之前还使用setParentClassLoader方法设置了一下父classloader。这里简单介绍一下有关classloader的一个重要
特性,就是如果classloader要load一个类时,不是自己先找,而是先把这个任务委派给自己的父classloader,然后自己的父classloader
也不找,在把这个任务委派给自己的父classloader,直到找到最顶层的classloader,然后再自顶向下的找对应的这个要load的类的定义,
如果那个classloader先找到,就返回。所以接合上面第一点介绍的tomcat中3个classloader,大家就可以明白tomca的classloader找类
的顺序了,这个对程序开发人员来说特别重要。我想使用过tomcat或者其他app server的兄弟肯定碰到过一个类明明存在可就是找不到,
或者总是找到一个老的版本,我想主要是在多个地方放置的原因,或者哪里有重名的类:-)
二.org.apache.catalina.startup.Catalina类
现在程序转到org.apache.catalina.startup.Catalina类里面的process方法。
这个方法首先设置一下catalina的home和base目录,然后通过arguments方法解析命令行参数,
最后调用execute()方法启动server。而execute方法很简单,就是根据arguments解析的命令行参数,
决定是启动server,还是stop server,如果是start server,就调用start方法,而下面重点讲一下这个start()方法,
因为才算是一个真正开始的启动tomcat的地方:-)
1. start方法首先使用Digester(这个东东是jakarta commons里面的一个用于解析xml文件的工具包,一开始是专门用于解析struts配置文件的,
后来被抽象成现在的一个通用工具,主要还是用来解析xml配置文件,根据一些定义的rule自动生成对应的类的实例,具体信息可以参考
apache网站上的文档)来设置tomcat配置文件,也就是/conf/server.xml这个文件的解析规则
然后通过如下代码来将配置文件中的数据转化成内存中的实例:
代码:
File file = configFile();
try {
InputSource is =
new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
System.exit(1);
}
转换的规则如下(我只作一些简单的介绍),例如配置文件中的
a. Server对应可以产成一个org.apache.catalina.core.StandardServer类(这个类很重要,是tomcat server的实现)
b. Server/GlobalNamingResources对应生成org.apache.catalina.deploy.NamingResources类
而大家比较熟悉的监听8080端口的类配置如下:
c. Server/Service/Connector:org.apache.catalina.connector.http.HttpConnector
d. Server/Service/Engine/Host/Context/:org.apache.catalina.core.StandardContext
有兴趣的兄弟可以参考jakarta commons里面的Digester文档和org.apache.catalina.startup.Catalina
这个类里面的createStartDigester方法.
在这段代码之后,一个叫server的变量已经通过Digester工具生成了,它将会用于启动tomcat。
2. 然后程序进行了一些server启动前的设置工作,例如重定向log输出流等等。而server启动的代码如下:
代码:
// Start the new server
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
try {
// Register shutdown hook
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Wait for the server to be told to shut down
server.await();
推荐文章 |
