在Flame ---- Chapter 2中的遗留问题尚未得到解决,在读取socket的inputstream中仍然会阻塞。在本chapter中尝试去阅读tomcat的启动代码, 了解tomcat如何处理这个问题的
tomcat的整体框架图如下图所示

Tomcat启动流程
if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
t.printStackTrace();
}
- 实例化Bootstrap对象daemon, 并init
1.1 指定init方法的时候主要执行如下流程- 初始化CatalinaHome, CatalinaBase等环境参数
- 初始化common.loader, server.loader, shared.loader等classloader对象,由createClassLoader完成
- 从catalina.properties(或者由catalina.config环境变量指定)文件中加载common.loader指定的类加载路径, 然后完成其中占位符的替换, 得到这些路径(repository)以及repository_type(is_jar, is_dir, is_glob等参数)
- 调用ClassLoaderFactory.createClassLoader完成classloader的构造, 最终会构造一个StandardClassLoader对象
- 这三个classLoader对象的关系是commonLoader下属为catalinaLoader和sharedLoader两个对象, 而最终commonLoader的parent为SystemClassLoader对象
- 创建Catalina对象,利用反射完成,称为startUpClass
- 调用Catalina对象的setParentClassLoader方法, 把sharedLoader作为参数传入
- 最后将BootStrap对象的catalinaDaemon设置为startUpInstance对象
- start
2.1 daemon.setAwait, 最终执行的是Catalina对象的setAwait方法
2.2 daemon.load, 最终执行的是Catalina对象的load方法- 初始化相关的文件夹,包括catalina.home, catalina.base, java.io.tmpdir等参数
- 初始化Digest对象及其前后置操作
- Digest是什么:Digester对象可以用于解析XML文档。对于XML文档中定义的每个元素,Digester对象都会检查它是不是要做预先定义的事件。在调用Digester对象的parse方法之前,程序员需要定义好Digetser对象执行哪些动作。【《How Tomcat Works》】
- 创建startDigester,并创建一些digester规则,用于解析server.xml文档。这些规则包括处理元素标签上定义的属性(properties),设置子元素(包括setServer, setService等函数),以及一些规则集合(EngineRuleSet, ContextRuleSet, NamingRuleSet, ClusterRuleSet等)
- 加载conf/server.xml文件
- 前置一些判断,包括判断tomcat文件路径, 以及是否为内嵌的tomcat
- digest对象执行parse函数,完成server.xml的解析, 实际上委托的Parser是SaxParserImpl对象。
- digester对象内部实现逻辑:使用SAX解析方式解析完一个标签,然后找到满足这个pattern的所有rule, 并执行这个rule, 其中值得注意的一个逻辑:digester内部有一个stack,然后使用这个stack存储所有ObjectCreateRule的对象。
- Digest对象首先将当前的Catalina对象放置到stack中,代码如下
digester.push(this); // 把Catalina对象放置到stack中 digester.parse(inputSource);
- Digester对象的SetNextRule实现规则
Object child = digester.peek(0); // 堆栈中从上到下第0个元素
Object parent = digester.peek(1); // 堆栈中从上到下第1个元素
if (digester.log.isDebugEnabled()) {
if (parent == null) {
digester.log.debug("[SetNextRule]{" + digester.match +
"} Call [NULL PARENT]." +
methodName + "(" + child + ")");
} else {
digester.log.debug("[SetNextRule]{" + digester.match +
"} Call " + parent.getClass().getName() + "." +
methodName + "(" + child + ")");
}
}
// Call the specified method
IntrospectionUtils.callMethod1(parent, methodName,
child, paramType, digester.getClassLoader());
可以发现是从堆栈从上取得前两个元素,然后调用第1个元素,关联第0个元素
2.3 daemon.start, 委托给catalina的start方法
-
((Lifecycle) getServer()).start()
(注意server是在Digester中注册得到的),进入StandardServer.start()- 先判断server的状态,防止多次start造成错误
-
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
- LifeCycle对象在创建StandardServer对象时创建,Digester对象定义的addNext规则把所有listener都添加到lifecycle中。
- 触发6个listener的BEFORE_START_EVENT事件,关于listener等组件的详细说明见《Flame ---- Chapter 5》
lifecycle.fireLifecycleEvent(START_EVENT, null);
- addShutdownHook(shutdownHook)
- 监听某个端口,允许执行shutdown操作
网友评论