1、通过catalina.sh start脚本启动,调用org.apache.catalina.startup.Bootstrap的main方法,传人参数start,首先是创建一个Bootstrap的实例并付给static变量daemon,然后调用daemon的init方法,在init方法中设置catalina-home,catalina-base,之后根据catalina.properties的配置创建commonLoader(父loader为空)、catalinaLoader、sharedLoader(server.loader,shared.loader默认为空,此时,catalinaLoader、sharedLoader使用其父loader即commonLoader),将catalinaLoader设为当前线程的上下文classloader,如果设置了SecurityManager,则preload一些class文件,然后调用catalinaLoader加载org.apache.catalina.startup.Catalina,并初始化一个实例startupInstance,赋值给catalinaDaemon,通过反射调用startupInstance的setParentClassLoader方法,将ParentClassLoader设置为sharedLoader。然后调用daemon的load方法,通过反射调用catalinaDaemon的load方法。最后调用daemon的start方法,通过反射调用catalinaDaemon的start方法。
2、catalina的load方法
首先初始化catalina-home等dir,初始化naming,然后创建digester,用来读取conf/server.xml配置文件,创建server、service等组件。
initDirs();
// Before digester - it may be needed
initNaming();
// Create and execute our Digester
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource("file://" + file.getAbsolutePath());
} catch (Exception e) {
;
}
tomcat初始化组件server、service、engine、host、context等,并设置包含关系的过程是通过开源的digester库来完成的,根据配置文件conf/server.xml中配置的信息来初始化各个组件,而不是通过硬编码的方式(配置有改动之后需要重新编译),提供了极大的灵活性。
在调用digester的parse方法处理xml之前,需要制定一系列的规则,例如遇到server、service元素时需要采取什么动作,做哪些关联等。例如:
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
上面的三条规则的第一个参数都是patten,即指定遇到这个server标签时采取的动作:
第一条当遇到Server标签时,创建一个server对象,如果没有制定className属性的话,则默认创建org.apache.catalina.core.StandardServer类的实例,否则根据制定的className属性来创建server,并压人digester的栈中;
第二条addSetProperties将digester的栈顶元素(即刚压人的server实例)出栈,并根据xml中配置的属性调用相应方法给server实例进行设置;
第三条addSetNext将栈顶的2个元素出栈,假设第一个出栈为a,第二个为b,则调用b的setServer方法,参数是a,而且a的Type为org.apache.catalina.Server。
这里有个问题,Server是server.xml的顶层标签,当遇到第一条规则的时候只向栈里压了一个元素,第三条规则要求出栈2个元素,栈底是哪个元素?
在load方法创建digester之后,调用parse之前,还压人了一个元素,代码如下:
digester.push(this);
digester.parse(inputSource);
即把Catalina实例本身压人栈中,Catalina继承了Embedded,Embedded又继承了StandardService,看见Catalina是一个service,这样就把Catalina的server变量指向了堆栈中的server实例。
由于server和service是一对多的关系,通过server.xml可以配置server标签包含service标签,即server指向service,而反向关系需要通过上述规则进行设置。
3、catalina的start方法
// Start the new server
if (getServer() instanceof Lifecycle) {
try {
((Lifecycle) getServer()).start();
} catch (LifecycleException e) {
log.error("Catalina.start: ", e);
}
}
getServer即第2步中通过第三条获得的Server实例,调用其start方法,将依次调用其组件的start方法。
之后设置退出钩子,当用户关闭tomcat窗口或者杀死进程时,启动CatalinaShutdownHook线程,调用stop方法。
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
然后主线程进入等待状态,监听8005端口,如果收到“SHUTDOWN”命令则退出。
tomcat启动过程分析