Tomcat启动过程分析
先来看看Tomcat的架构吧:
本文将介绍Tomcat启动时涉及到的组件和初始化,启动过程。
1:Tomcat启动从org.apache.catalina.startup的main()函数开始。

- <span style=”font-size:18px;”>public static void main(String args[]) {
- if (daemon == null) {
- daemon = new Bootstrap();
- try {
- daemon.init();
- } catch (Throwable t) {
- t.printStackTrace();
- return;
- }
- }
- …..此处省略部分代码…
- daemon.load(args);
- daemon.start();
- …..此处省略部分代码…
- }</span>
此处看到启动过程涉及到3个函数:init(),load(),start()。先来分析一下init()吧。

- <span style=”font-size:18px;”>public void init()
- throws Exception
- {
- …..此处省略部分代码…….
- initClassLoaders();
- …..此处省略部分代码…….
- //下面通过反射机制调用Catalina的setParentClassLoader方法
- Class startupClass =
- catalinaLoader.loadClass
- (“org.apache.catalina.startup.Catalina”);
- Object startupInstance = startupClass.newInstance();
- // Set the shared extensions class loader
- if (log.isDebugEnabled())
- log.debug(“Setting startup class properties”);
- String methodName = “setParentClassLoader”;
- Class paramTypes[] = new Class[1];
- paramTypes[0] = Class.forName(“java.lang.ClassLoader”);
- Object paramValues[] = new Object[1];
- paramValues[0] = sharedLoader;
- Method method =
- startupInstance.getClass().getMethod(methodName, paramTypes);
- method.invoke(startupInstance, paramValues);
- catalinaDaemon = startupInstance;
- }</span>

- <span style=”font-size:18px;”>private void initClassLoaders() {
- try {
- commonLoader = createClassLoader(“common”, null);
- if( commonLoader == null ) {
- // no config file, default to this loader – we might be in a ‘single’ env.
- commonLoader=this.getClass().getClassLoader();
- }
- catalinaLoader = createClassLoader(“server”, commonLoader);
- sharedLoader = createClassLoader(“shared”, commonLoader);
- } catch (Throwable t) {
- log.error(“Class loader creation threw exception”, t);
- System.exit(1);
- }
- }</span>
initClassLoaders() 初始化了三个类加载器。接下来catalina就可以用这三种类型的classLoader来负责装配容器了。
接下来是load()方法。它其实会通过反射机制去执行catalina的load()方法。

- <span style=”font-size:18px;”>public void load() {
- //……
- //初始化Digester组件,定义了解析规则
- Digester digester = createStartDigester();
- //……中间略去代码若干,主要作用为将server.xml文件转换为输入流
- try {
- inputSource.setByteStream(inputStream);
- digester.push(this);
- //通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系
- digester.parse(inputSource);
- inputStream.close();
- } catch (Exception e) {
- }
- // 调用Server的initialize方法,初始化各个组件
- if (getServer() instanceof Lifecycle) {
- try {
- getServer().initialize();
- } catch (LifecycleException e) {
- if (Boolean.getBoolean(“org.apache.catalina.startup.EXIT_ON_INIT_FAILURE”))
- throw new java.lang.Error(e);
- else
- log.error(“Catalina.start”, e);
- }
- }
- } </span>
可以看出load的两个任务:使用Digester组件按照给定的规则解析server.xml、调用Server的initialize方法。这里的Server是从StandardService中的getServer()得来的。让我们进入Server的init()中吧。init()——>initialize()。

- <span style=”font-size:18px;”>public void initialize()
- throws LifecycleException
- {
- ……此处省略部分代码……
- lifecycle.fireLifecycleEvent(INIT_EVENT, null);
- initialized = true;
- ……此处省略部分代码……
- // Initialize our defined Services
- for (int i = 0; i < services.length; i++) {
- services[i].initialize();
- }
- }</span>
如果你还有兴趣追溯下去的话,你会发现services的initialize()中还会进行connector的初始化等。这样就能完成各级组件的初始化。
接下来是start()函数了。

- <span style=”font-size:18px;”>public void start()
- throws Exception {
- if( catalinaDaemon==null ) init();
- Method method = catalinaDaemon.getClass().getMethod(“start”, (Class [] )null);
- method.invoke(catalinaDaemon, (Object [])null);
- }</span>
可以看出还是通过反射机制调用catalina的start()方法。

- <span style=”font-size:18px;”>public void start() {
- ……此处省略部分代码……
- // Start the new server
- if (getServer() instanceof Lifecycle) {
- try {
- ((Lifecycle) getServer()).start();
- } catch (LifecycleException e) {
- log.error(“Catalina.start: “, e);
- }
- }
- ……此处省略部分代码……
- try {
- // Register shutdown hook
- if (useShutdownHook) {
- if (shutdownHook == null) {
- shutdownHook = new CatalinaShutdownHook();
- }
- Runtime.getRuntime().addShutdownHook(shutdownHook);
- ……此处省略部分代码……
- }</span>
当你看到了

- <span style=”font-size:18px;”>if (getServer() instanceof Lifecycle) {
- try {
- ((Lifecycle) getServer()).start();
- } catch (LifecycleException e) {
- log.error(“Catalina.start: “, e);
- }
- }</span>
这段代码时,你很容易联想到我们之前的load方法,当时是采用级联方式的进行load的。同样这里也是那样。不断的通过级联进行start。那么service,Engine,Host,Connector等组件容器都会启动。shutdownHook 是我们注册的一个Hook。tomcat中提供了hook,可以在关闭过程中运行一些代码,执行清理的工作。shutdown hook是一个java.lang.Thread子类的实例,shutdown hook会由jvm启动,不用你自己启动。

- <span style=”font-size:18px;”>protected class CatalinaShutdownHook extends Thread {
- public void run() {
- ……此处省略部分代码……
- }
- }</span>
这样就完成了整个Tomcat的启动了。当客户端有请求进来了,就可以通过connector等组件的配合完成整个的请求处理。