Tomcat启动过程分析

先来看看Tomcat的架构吧:

本文将介绍Tomcat启动时涉及到的组件和初始化,启动过程。

1:Tomcat启动从org.apache.catalina.startup的main()函数开始。

 

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>public static void main(String args[]) {
  2.         if (daemon == null) {
  3.             daemon = new Bootstrap();
  4.             try {
  5.                 daemon.init();
  6.             } catch (Throwable t) {
  7.                 t.printStackTrace();
  8.                 return;
  9.             }
  10.         }
  11.         …..此处省略部分代码…
  12.                 daemon.load(args);
  13.                 daemon.start();
  14.         …..此处省略部分代码…
  15.     }</span>

此处看到启动过程涉及到3个函数:init(),load(),start()。先来分析一下init()吧。

 

 

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>public void init()
  2.         throws Exception
  3.     {
  4. …..此处省略部分代码…….
  5.         initClassLoaders();
  6. …..此处省略部分代码…….
  7. //下面通过反射机制调用Catalina的setParentClassLoader方法
  8.         Class startupClass =
  9.             catalinaLoader.loadClass
  10.             (“org.apache.catalina.startup.Catalina”);
  11.         Object startupInstance = startupClass.newInstance();
  12.         // Set the shared extensions class loader
  13.         if (log.isDebugEnabled())
  14.             log.debug(“Setting startup class properties”);
  15.         String methodName = “setParentClassLoader”;
  16.         Class paramTypes[] = new Class[1];
  17.         paramTypes[0] = Class.forName(“java.lang.ClassLoader”);
  18.         Object paramValues[] = new Object[1];
  19.         paramValues[0] = sharedLoader;
  20.         Method method =
  21.             startupInstance.getClass().getMethod(methodName, paramTypes);
  22.         method.invoke(startupInstance, paramValues);
  23.         catalinaDaemon = startupInstance;
  24.     }</span>
[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>private void initClassLoaders() {
  2.         try {
  3.             commonLoader = createClassLoader(“common”, null);
  4.             if( commonLoader == null ) {
  5.                 // no config file, default to this loader – we might be in a ‘single’ env.
  6.                 commonLoader=this.getClass().getClassLoader();
  7.             }
  8.             catalinaLoader = createClassLoader(“server”, commonLoader);
  9.             sharedLoader = createClassLoader(“shared”, commonLoader);
  10.         } catch (Throwable t) {
  11.             log.error(“Class loader creation threw exception”, t);
  12.             System.exit(1);
  13.         }
  14.     }</span>

initClassLoaders() 初始化了三个类加载器。接下来catalina就可以用这三种类型的classLoader来负责装配容器了。

 

接下来是load()方法。它其实会通过反射机制去执行catalina的load()方法。

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>public void load() {
  2. //……
  3.         //初始化Digester组件,定义了解析规则
  4.         Digester digester = createStartDigester();
  5.         //……中间略去代码若干,主要作用为将server.xml文件转换为输入流
  6.         try {
  7.             inputSource.setByteStream(inputStream);
  8.             digester.push(this);
  9. //通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系
  10.             digester.parse(inputSource);
  11.             inputStream.close();
  12.         } catch (Exception e) {
  13.         }
  14.         // 调用Server的initialize方法,初始化各个组件
  15.         if (getServer() instanceof Lifecycle) {
  16.             try {
  17.                 getServer().initialize();
  18.             } catch (LifecycleException e) {
  19.                 if (Boolean.getBoolean(“org.apache.catalina.startup.EXIT_ON_INIT_FAILURE”))
  20.                     throw new java.lang.Error(e);
  21.                 else
  22.                     log.error(“Catalina.start”, e);
  23.             }
  24.         }
  25.     } </span>

可以看出load的两个任务:使用Digester组件按照给定的规则解析server.xml、调用Server的initialize方法。这里的Server是从StandardService中的getServer()得来的。让我们进入Server的init()中吧。init()——>initialize()。

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>public void initialize()
  2.         throws LifecycleException
  3.     {
  4. ……此处省略部分代码……
  5.         lifecycle.fireLifecycleEvent(INIT_EVENT, null);
  6.         initialized = true;
  7. ……此处省略部分代码……
  8.         // Initialize our defined Services
  9.         for (int i = 0; i < services.length; i++) {
  10.             services[i].initialize();
  11.         }
  12.     }</span>

如果你还有兴趣追溯下去的话,你会发现services的initialize()中还会进行connector的初始化等。这样就能完成各级组件的初始化。

 

接下来是start()函数了。

 

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>public void start()
  2.         throws Exception {
  3.         if( catalinaDaemon==null ) init();
  4.         Method method = catalinaDaemon.getClass().getMethod(“start”, (Class [] )null);
  5.         method.invoke(catalinaDaemon, (Object [])null);
  6.     }</span>

可以看出还是通过反射机制调用catalina的start()方法。

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>public void start() {
  2. ……此处省略部分代码……
  3.         // Start the new server
  4.         if (getServer() instanceof Lifecycle) {
  5.             try {
  6.                 ((Lifecycle) getServer()).start();
  7.             } catch (LifecycleException e) {
  8.                 log.error(“Catalina.start: “, e);
  9.             }
  10.         }
  11.  ……此处省略部分代码……
  12.         try {
  13.             // Register shutdown hook
  14.             if (useShutdownHook) {
  15.                 if (shutdownHook == null) {
  16.                     shutdownHook = new CatalinaShutdownHook();
  17.                 }
  18.                 Runtime.getRuntime().addShutdownHook(shutdownHook);
  19.  ……此处省略部分代码……
  20.     }</span>

当你看到了

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>if (getServer() instanceof Lifecycle) {
  2.             try {
  3.                 ((Lifecycle) getServer()).start();
  4.             } catch (LifecycleException e) {
  5.                 log.error(“Catalina.start: “, e);
  6.             }
  7.         }</span>

这段代码时,你很容易联想到我们之前的load方法,当时是采用级联方式的进行load的。同样这里也是那样。不断的通过级联进行start。那么service,Engine,Host,Connector等组件容器都会启动。shutdownHook 是我们注册的一个Hook。tomcat中提供了hook,可以在关闭过程中运行一些代码,执行清理的工作。shutdown hook是一个java.lang.Thread子类的实例,shutdown hook会由jvm启动,不用你自己启动。

 

 

[java][/java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. <span style=”font-size:18px;”>protected class CatalinaShutdownHook extends Thread {
  2.         public void run() {
  3.             ……此处省略部分代码……
  4.         }
  5.     }</span>

这样就完成了整个Tomcat的启动了。当客户端有请求进来了,就可以通过connector等组件的配合完成整个的请求处理。

标签