启动Tomcat

  这篇随笔的重点关注启动Tomcat时会用到的两个类,分别是Catalina类 和 Bootstrap类,它们都位于org.apache.catalina.startup包下,Catalina类用于启动或关闭Server对象,并负责解析Tomcat文件:server.xml文件。Bootstrap类是一个入口点,负责创建Catalina实例,并调用其process()方法,理论上这两个类可以合并为一个,但是为了支持Tomcat的多种运行模式,而提供了多种启动类,例如,上面说到的Bootstrap类是作为一个独立的应用程序运行Tomcat的,而另一个类org.apahce.catalina.starup.BootstrapService可以使Tomcat作为一个Windows NT服务来运行。

  为了使用户使用方便,Tomcat附带了批处理文件 和 shell 脚本,可以方便的启动或者关闭servlet容器,在这些批处理 文件 和  shell 脚本的帮助下, 用户无须为了执行Bootstrap类 而记下 java.exe程序的选项,相反,用户只需要运行相应的批处理文件或者 shell脚本即可,

先介绍一下Catalina类

 Catalina类

  org.apache.catalina.startup.Catalina类是启动类,它包含了一个Digester对象,用于解析位于%CATALIN_HOME%conf目录下的server.xml文件。理解了添加到Digester对象中的规则之后,就可以自行配置Tomcat了。

  Catalina类还封装了一个Server对象,该对象有一个Service对象,正如之前学习的那样,Service对象包含一个Servlet容器 和 一个 或者多个连接器,可以使用Catalina类来启动或者关闭Server对象,

  可以通过实例化Catalina类,并调用其process方法来运行Tomcat,但是在调用该方法时,需要传入适当的参数,第一个参数是start 表示要启动Tomcat,或 stop 表示要向Tomcat发送一条关闭命令,还有其他的参数可选,包括-help、-config、-debug和-nothing

  注意:当使用nothing参数时,表示将不对JNDI 命名提供支持,更多关于Tomcat中 对JNDI命名的支持 看org.apahce.naming包中的类吧

  一般情况下,及时Catalina类提供了main方法作为程序的入口点,也需要使用Bootstrap类来实例化Catalina类,并调用其process方法,下面给出procee的实现

 

/**
     * 实例主程序。
     *
     * @param args Command line arguments
     */
    public void process(String args[]) {

        setCatalinaHome();
        setCatalinaBase();
        try {
            if (arguments(args))
                execute();
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }

process 方法设置了两个系统属性,分别是 catalina.home 和 catalina.base,默认值均与user.dir值相同,

注意:user.dir属性的值指明了用户的工作目录,即,会从哪个目录下调用java命令,更多系统属性的列表,可以参见java.lang.System类 getProperties方法的介绍

然后procee 方法会调用arguments方法,并传入参数列表,arguments方法处理命令行参数,如果Catalina对象能够继续处理的话,arguments方法会返回true,

 

/**
     * 处理指定的命令行参数,如果应该继续处理,则返回true;否则返回false。
     *
     * @param args
     *            Command line arguments to process
     */
    protected boolean arguments(String args[]) {

        boolean isConfig = false;

        if (args.length < 1) {
            usage();
            return (false);
        }

        for (int i = 0; i < args.length; i++) {
            if (isConfig) {
                configFile = args[i];
                isConfig = false;
            } else if (args[i].equals("-config")) {
                isConfig = true;
            } else if (args[i].equals("-debug")) {
                debug = true;
            } else if (args[i].equals("-nonaming")) {
                useNaming = false;
            } else if (args[i].equals("-help")) {
                usage();
                return (false);
            } else if (args[i].equals("start")) {
                starting = true;
            } else if (args[i].equals("stop")) {
                stopping = true;
            } else {
                usage();
                return (false);
            }
        }

        return (true);

    }

   process方法会检查arguments方法的返回值,如果返回值为true,则调用execute方法

 1 /**
 2      * 执行 命令行配置后 处理。
 3      * 
 4      * 
 5      */
 6     protected void execute() throws Exception {
 7 
 8         // 如果命令启动则启动
 9         if (starting)
10             start();
11         // 如果命令停止则停止
12         else if (stopping)
13             stop();
14 
15     }

execute方法会调用start方法 启动Tomcat 或者 调用stop方法来关闭Tomcat。

注意:在Tomcat 5 以及之后,没有execute方法,会在procee方法中调用 start方法 或者 stop方法

Start方法

  start方法 会创建一个Digester实例来解析server.xml文件(Tomcat 配置文件),在解析 server.xml文件之前,start方法会调用Digester对象的push方法,传入当前Catalin对象作为参数,这样Catalina对象就成为了 Digester对象的内部栈中的第一个对象,解析Server.xml文件之后,会使变量server引用一个Server对象,默认是org.apache.catalina.core.StandardServer类型的对象,然后start方法会调用Server对象的initialize方法 和 start方法,接着,Catalina对象的start方法会调用Server对象的await反方循环等待,直到接收到正确的关闭命令,当await方法返回时,Catalina对象的start方法会调用Server对象的stop方法,从而关闭Server对象和其他的组件,此外 start方法 还会使用关闭钩子,确保用户突然退出应用程序时会执行Server对象的stop方法。

 

  1 /**
  2      * 启动一个新的Server实例
  3      */
  4     protected void start() {
  5 
  6         // 创建一个解析XML文件的Digester
  7         Digester digester = createStartDigester();
  8         // 包含 服务器配置文件的File引用
  9         File file = configFile();
 10         try {
 11             InputSource is = new InputSource("file://" + file.getAbsolutePath());
 12             FileInputStream fis = new FileInputStream(file);
 13             is.setByteStream(fis);
 14             // 将该Catalina对象压入Digester对象的内部栈中,成为内部栈中的第一个元素
 15             digester.push(this);
 16             digester.parse(is);
 17             fis.close();
 18         } catch (Exception e) {
 19             System.out.println("Catalina.start: " + e);
 20             e.printStackTrace(System.out);
 21             System.exit(1);
 22         }
 23 
 24         // 设置附加变量
 25         if (!useNaming) {
 26             System.setProperty("catalina.useNaming", "false");
 27         } else {
 28             System.setProperty("catalina.useNaming", "true");
 29             String value = "org.apache.naming";
 30             String oldValue = System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
 31             if (oldValue != null) {
 32                 value = value + ":" + oldValue;
 33             }
 34             System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
 35             value = System.getProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
 36             if (value == null) {
 37                 System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
 38                         "org.apache.naming.java.javaURLContextFactory");
 39             }
 40         }
 41 
 42         // If a SecurityManager is being used, set properties for
 43         // checkPackageAccess() and checkPackageDefinition
 44         if (System.getSecurityManager() != null) {
 45             String access = Security.getProperty("package.access");
 46             if (access != null && access.length() > 0)
 47                 access += ",";
 48             else
 49                 access = "sun.,";
 50             Security.setProperty("package.access", access + "org.apache.catalina.,org.apache.jasper.");
 51             String definition = Security.getProperty("package.definition");
 52             if (definition != null && definition.length() > 0)
 53                 definition += ",";
 54             else
 55                 definition = "sun.,";
 56             Security.setProperty("package.definition",
 57                     // FIX ME package "javax." was removed to prevent HotSpot
 58                     // fatal internal errors
 59                     definition + "java.,org.apache.catalina.,org.apache.jasper.");
 60         }
 61 
 62         // Replace System.out and System.err with a custom PrintStream
 63         SystemLogHandler log = new SystemLogHandler(System.out);
 64         System.setOut(log);
 65         System.setErr(log);
 66 
 67         // 创建一个关闭钩子
 68         Thread shutdownHook = new CatalinaShutdownHook();
 69 
 70         // 初始化 Server对象
 71         if (server instanceof Lifecycle) {
 72             try {
 73                 server.initialize();
 74                 ((Lifecycle) server).start();
 75                 try {
 76                     // 注册关闭钩子
 77                     Runtime.getRuntime().addShutdownHook(shutdownHook);
 78                 } catch (Throwable t) {
 79                     // This will fail on JDK 1.2. Ignoring, as Tomcat can run
 80                     // fine without the shutdown hook.
 81                 }
 82                 // 等待关闭命令 阻塞 直到 收到正确的关闭命令
 83                 server.await();
 84             } catch (LifecycleException e) {
 85                 System.out.println("Catalina.start: " + e);
 86                 e.printStackTrace(System.out);
 87                 if (e.getThrowable() != null) {
 88                     System.out.println("----- Root Cause -----");
 89                     e.getThrowable().printStackTrace(System.out);
 90                 }
 91             }
 92         }
 93 
 94         // 关闭服务器组件
 95         if (server instanceof Lifecycle) {
 96             try {
 97                 try {
 98                     // 首先删除关闭钩子,以便server.stop()
 99 
100                     // 不会被调用两次
101                     Runtime.getRuntime().removeShutdownHook(shutdownHook);
102                 } catch (Throwable t) {
103                     // This will fail on JDK 1.2. Ignoring, as Tomcat can run
104                     // fine without the shutdown hook.
105                 }
106                 // 关闭服务器组件
107                 ((Lifecycle) server).stop();
108             } catch (LifecycleException e) {
109                 System.out.println("Catalina.stop: " + e);
110                 e.printStackTrace(System.out);
111                 if (e.getThrowable() != null) {
112                     System.out.println("----- Root Cause -----");
113                     e.getThrowable().printStackTrace(System.out);
114                 }
115             }
116         }
117 
118     }

 

stop方法

  stop方法用来关闭Catalina 和 Server对象

 1 /**
 2      * 停止现有的服务器实例。 其实就是手动 向 服务器组件负责监听关闭命令的端口发送 关闭名命令
 3      */
 4     protected void stop() {
 5 
 6         // 创建一个解析服务器组件配置XML文件的Digester
 7         Digester digester = createStopDigester();
 8         File file = configFile();
 9         try {
10             InputSource is = new InputSource("file://" + file.getAbsolutePath());
11             FileInputStream fis = new FileInputStream(file);
12             is.setByteStream(fis);
13             digester.push(this);
14             digester.parse(is);
15             fis.close();
16         } catch (Exception e) {
17             System.out.println("Catalina.stop: " + e);
18             e.printStackTrace(System.out);
19             System.exit(1);
20         }
21 
22         // 向服务器组件 指定监听关闭命令端口发送关闭命令
23         try {
24             Socket socket = new Socket("127.0.0.1", server.getPort());
25             OutputStream stream = socket.getOutputStream();
26             String shutdown = server.getShutdown();
27             for (int i = 0; i < shutdown.length(); i++)
28                 stream.write(shutdown.charAt(i));
29             stream.flush();
30             stream.close();
31             socket.close();
32         } catch (IOException e) {
33             System.out.println("Catalina.stop: " + e);
34             e.printStackTrace(System.out);
35             // 如果发送命令时异常 则直接退出 使用关闭钩子来关闭服务器组件
36             System.exit(1);
37         }
38 
39     }

 

启动Digester对象

  Catalina类的createStartDigester方法创建了一个Digester实例,然后为其添加规则,以解析server.xml文件,server.xml文件用来配置Tomcat,位于%CATALINA_HOME%/conf目录下,添加到Digester对象中的规则 是理解Tomcat配置的关键,

/**
     * 创建和配置我们将用于启动的Digester
     */
    protected Digester createStartDigester() {

        // 初始化Digester
        Digester digester = new Digester();
        if (debug)
            digester.setDebug(999);
        // 不校验XML的格式内容
        digester.setValidating(false);

        // 配置我们将要使用的操作
        // 遇到Server模式时,使用Server元素的className的值来创建Server实例
        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
        // 配置Server 各种属性
        digester.addSetProperties("Server");
        // 将Server实例 与当前Catalina对象关联上,利用setServer方法,将Server实例传入,Server对象类型为
        // "org.apache.catalina.Server"
        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

        // 遇到Server/GlobalNamingResources模式时,创建
        // org.apache.catalina.deploy.NamingResources实例
        digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
        // 设置模式 Server/GlobalNamingResources 生成的对象各种属性
        digester.addSetProperties("Server/GlobalNamingResources");
        // 将其与Server对象关联起来,利用 Server对象的setGlobalNamingResources方法传入
        // GlobalNamingResources类型实例
        digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources",
                "org.apache.catalina.deploy.NamingResources");
                // xml规则到这里 如果存在上面 Server/GlobalNamingResources元素 到这里可定会遇到结束元素标签
                // 会将该值从内部栈中移除

        // 遇到 Server/Listener 必须制定 Listener实现类
        digester.addObjectCreate("Server/Listener", null, // MUST be specified
                                                            // in the element
                "className");
        // 配置Listener对象属性
        digester.addSetProperties("Server/Listener");
        // 将Listener与Server对象关联起来通过 addLifecycleListener
        digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
        // 同样 在这里 如果存在 Listener标签 也会遇到结束标签 然后将 Listener对象移除从内部栈中

        // 遇到Server/Service模式 创建 Service
        digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
        // 真滴累 不想在写了 如果熟悉 Digester的人其实不用再写了 如果不熟悉的话 可以参考之前写的随笔Digester
        digester.addObjectCreate("Server/Service/Listener", null, // MUST be
                                                                    // specified
                                                                    // in the
                                                                    // element
                "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");

        digester.addObjectCreate("Server/Service/Connector", "org.apache.catalina.connector.http.HttpConnector",
                "className");
        digester.addSetProperties("Server/Service/Connector");
        digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.Connector");

        digester.addObjectCreate("Server/Service/Connector/Factory",
                "org.apache.catalina.net.DefaultServerSocketFactory", "className");
        digester.addSetProperties("Server/Service/Connector/Factory");
        digester.addSetNext("Server/Service/Connector/Factory", "setFactory",
                "org.apache.catalina.net.ServerSocketFactory");

        digester.addObjectCreate("Server/Service/Connector/Listener", null, // MUST
                                                                            // be
                                                                            // specified
                                                                            // in
                                                                            // the
                                                                            // element
                "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener",
                "org.apache.catalina.LifecycleListener");

        // 为嵌套元素添加规则集
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(digester, parentClassLoader));

        return (digester);

    }

 

  上述方法,会创建一个org.apache.commons.digester.Digester类的一个实例,并且为其添加规则。

  前三条规则用于解析server.xml文件的Server元素,可能你已经知道了Server元素时Server.xml文件的根元素,下面是为Server模式添加的规则;

1 // 配置我们将要使用的操作
2         // 遇到Server模式时,使用Server元素的className的值来创建Server实例
3         digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
4         // 配置Server 各种属性
5         digester.addSetProperties("Server");
6         // 将Server实例 与当前Catalina对象关联上,利用setServer方法,将Server实例传入,Server对象类型为
7         // "org.apache.catalina.Server"
8         digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

  第一条规则:在遇到Server元素时,Digester对象要创建 org.apahce.catalina.core.StandardServer类的一个实例,例外是,如果Server元素有一个名为 className的属性,那么className属性的值就是必须实例化类的名称。

  第二条规则:指明要对Server对象的指定的属性名设置同名的java属性值,

  第三条规则将:Server对象压入到Digester对象内部栈中,并与栈中的下一个对象相关联,就是上一个被添加的Catalina对象,调用其setServer方法与Server对象相关联,那Catalina实例是如何放到Digester对象的内部栈中的呢,在Start方法的开始部分,在解析xml文件之前会调用Digester对象的push方法将Catalina对象压入栈

// 将该Catalina对象压入Digester对象的内部栈中,成为内部栈中的第一个元素
            digester.push(this);

现在你应该可以理解 后面规则的意思了 如果你还理解不了的话,看下前面的Digester随笔的内容。

关闭Digester对象

  createStopDigester方法返回一个Digester对象来关闭Server对象

 1     /**
 2      * 创建和配置我们将用于关闭的Digester。 因为我们只是为了获取到Server对象的 元素配置属性,目前用到的是 监听关闭命令的端口 以及
 3      * 关闭命令,所以只要解析Server元素 标签就可以了
 4      * 
 5      */
 6     protected Digester createStopDigester() {
 7 
 8         // Initialize the digester
 9         Digester digester = new Digester();
10         if (debug)
11             digester.setDebug(999);
12 
13         // 配置关闭所需的规则
14         digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
15         digester.addSetProperties("Server");
16         digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
17 
18         return (digester);
19 
20     }

  与启动Digester对象不同,关闭Digester对象只是对XML文档的根元素感兴趣 因为只是为了为了关闭时用到的Server的 监听关闭命令端口 以及 关闭命令

Bootstrap类

  有一些类提供了启动Tomcat的入口点,其中之一是org.apache.startup.Bootstrap类。当运行startup.bat文件或者startup.sh文件时,实际上就是调用了该类的main方法。main方法会创建3个类载入器,并实例化Catalina类,然后它调用Catalina的process方法。

  1 package org.apache.catalina.startup;
  2 
  3 import java.io.File;
  4 import java.lang.reflect.Method;
  5 
  6 /**
  7  * 
  8  * <p>
  9  * <b>Title:Bootstrap.java</b>
 10  * </p>
 11  * <p>
 12  * Copyright:ChenDong 2018
 13  * </p>
 14  * <p>
 15  * Company:仅学习时使用
 16  * </p>
 17  * <p>
 18  * 类功能描述:Catalina 的Boostrap装载类。这个应用程序构造了一个类加载器,用于加载Catalina内部类(通过
 19  * “catalina.home”下的
 20  * “server”目录中找到的所有JAR文件),并开始容器的常规执行。这种迂回方法的目的是将Catalina内部类(以及它们依赖的任何其他类,
 21  * 例如XML解析器)排除在系统类路径之外,因此应用程序级类不可见。
 22  * </p>
 23  * 
 24  * @author 陈东
 25  * @date 2018年12月25日 下午9:42:57
 26  * @version 1.0
 27  */
 28 public final class Bootstrap {
 29 
 30     // ------------------------------------------------------- Static Variables
 31 
 32     /**
 33      * 调试用于处理启动的细节级别。
 34      * 
 35      * 
 36      */
 37     private static int debug = 0;
 38 
 39     // ----------------------------------------------------------- Main Program
 40 
 41     /**
 42      * 引导程序的主程序。
 43      *
 44      * @param args
 45      *            Command line arguments to be processed
 46      */
 47     public static void main(String args[]) {
 48 
 49         // Set the debug flag appropriately
 50         for (int i = 0; i < args.length; i++) {
 51             if ("-debug".equals(args[i]))
 52                 debug = 1;
 53         }
 54 
 55         // 如果尚未设置,则从catalina.home配置catalina.base
 56         if (System.getProperty("catalina.base") == null)
 57             System.setProperty("catalina.base", getCatalinaHome());
 58         /**
 59          * 使用多个类载入器的母的是为了防止应用程序中类(包括 servlet类 和 Web应用程序中的其他辅助类)
 60          * 使用WEB-INF/classes目录 和 WEB-INF/lib目录之外的类。
 61          * 部署到%CATALINA_HOME%/common/lib目录下的jar文件的类文件是可用的
 62          */
 63 
 64         // 构造我们需要的类装入器
 65 
 66         ClassLoader commonLoader = null;
 67         ClassLoader catalinaLoader = null;
 68         ClassLoader sharedLoader = null;
 69         /**
 70          * 对于每个类载入器都会制定一条可以访问的路径,
 71          */
 72         try {
 73 
 74             File unpacked[] = new File[1];
 75             File packed[] = new File[1];
 76             File packed2[] = new File[2];
 77             ClassLoaderFactory.setDebug(debug);
 78 
 79             unpacked[0] = new File(getCatalinaHome(), "common" + File.separator + "classes");
 80             packed2[0] = new File(getCatalinaHome(), "common" + File.separator + "endorsed");
 81             packed2[1] = new File(getCatalinaHome(), "common" + File.separator + "lib");
 82             /**
 83              * commonLoader 类载入器,载入 <code>%CATALINA_HOME%/common/lib</code>、
 84              * <code>%CATALINA_HOME%/common/classes</code>、
 85              * <code>%CATALINA_HOME%/common/endorsed</code>目录下的java类
 86              */
 87             commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null);
 88 
 89             unpacked[0] = new File(getCatalinaHome(), "server" + File.separator + "classes");
 90             packed[0] = new File(getCatalinaHome(), "server" + File.separator + "lib");
 91             /**
 92              * catalinaLoaderb 类载入 负责 载入 运行 Catalina Servlet容器 所需要类,它可以载入
 93              * <code>%CATALINA_HOME%/server/classes</code>、
 94              * <code>%CATALINA_HOME%/server/lib</code> 以及 commonLoader
 95              * 类载入器可以载入的类
 96              */
 97             catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);
 98 
 99             unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator + "classes");
100             packed[0] = new File(getCatalinaBase(), "shared" + File.separator + "lib");
101             /**
102              * sharedLoader 类载入器可以载入
103              * <code>%CATALINA_HOME%/shared/classes</code>、
104              * <code>%CATALINA_HOME%/shared/lib</code> 以及 commonLoader
105              * 类载入器可以载入的类。
106              * 
107              * 在Tomcat 中,每个Web应用程序中与Context 容器相关联的每个类载入器的 父类载入器都是 sharedLoader
108              * 类载入器
109              * 
110              * sharedLoader 类载入器 并不能访问Catalina的内部类,或CLASSPATH环境变量指定的类路径中的类
111              */
112             sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);
113         } catch (Throwable t) {
114 
115             log("Class loader creation threw exception", t);
116             System.exit(1);
117 
118         }
119 
120         Thread.currentThread().setContextClassLoader(catalinaLoader);
121 
122         // 加载启动类并调用它的process()方法
123         try {
124 
125             SecurityClassLoad.securityClassLoad(catalinaLoader);
126 
127             // 实例化一个启动类Catalina类
128             if (debug >= 1)
129                 log("Loading startup class");
130             Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
131             Object startupInstance = startupClass.newInstance();
132 
133             // 设置共享扩展类加载器
134             if (debug >= 1)
135                 log("Setting startup class properties");
136             // 调用Catalina的setParentClassLoader方法
137             String methodName = "setParentClassLoader";
138             Class paramTypes[] = new Class[1];
139             paramTypes[0] = Class.forName("java.lang.ClassLoader");
140             Object paramValues[] = new Object[1];
141             paramValues[0] = sharedLoader;
142             Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
143             method.invoke(startupInstance, paramValues);
144 
145             // 调用Catalina的process
146             if (debug >= 1)
147                 log("Calling startup class process() method");
148             methodName = "process";
149             paramTypes = new Class[1];
150             paramTypes[0] = args.getClass();
151             paramValues = new Object[1];
152             paramValues[0] = args;
153             method = startupInstance.getClass().getMethod(methodName, paramTypes);
154             // 将参数args 传入Catalina对象的 process方法中
155             method.invoke(startupInstance, paramValues);
156 
157         } catch (Exception e) {
158             System.out.println("Exception during startup processing");
159             e.printStackTrace(System.out);
160             System.exit(2);
161         }
162 
163     }
164 
165     /**
166      * 获取catalina.home环境变量的值。 如果之前没有设置过{@code catalina.home}的值,就会返回 user.dir属性的值
167      */
168     private static String getCatalinaHome() {
169         return System.getProperty("catalina.home", System.getProperty("user.dir"));
170     }
171 
172     /**
173      * 获取catalina.base环境变量的值。
174      * 如果属性{@code catalina.base }的属性值为空,则返回{@code catalina.home}的值
175      */
176     private static String getCatalinaBase() {
177         return System.getProperty("catalina.base", getCatalinaHome());
178     }
179 
180     /**
181      * 记录调试详细信息。
182      *
183      * 
184      * 
185      * @param message
186      *            要被记录的信息
187      */
188     private static void log(String message) {
189 
190         System.out.print("Bootstrap: ");
191         System.out.println(message);
192 
193     }
194 
195     /**
196      * 记录异常的调试详细信息。
197      *
198      * 
199      * 
200      * @param message
201      *            要被记录的信息
202      * @param exception
203      *            记录时遇到异常
204      */
205     private static void log(String message, Throwable exception) {
206 
207         log(message);
208         exception.printStackTrace(System.out);
209 
210     }
211 
212 }

  Bootstrap 类 有四个静态方法, 分别是两个log方法、getCatalinaHome方法 和 getCatalinaBase()方法,getCatalinaHome方法的额实现如下

/**
     * 获取catalina.home环境变量的值。 如果之前没有设置过{@code catalina.home}的值,就会返回 user.dir属性的值
     */
    private static String getCatalinaHome() {
        return System.getProperty("catalina.home", System.getProperty("user.dir"));
    }

  getCatalinaBase方法实现如下

/**
     * 获取catalina.base环境变量的值。
     * 如果属性{@code catalina.base }的属性值为空,则返回{@code catalina.home}的值
     */
    private static String getCatalinaBase() {
        return System.getProperty("catalina.base", getCatalinaHome());
    }

此外  main方法 还会为不同目的而创建三个类载入器

    /**
         * 使用多个类载入器的母的是为了防止应用程序中类(包括 servlet类 和 Web应用程序中的其他辅助类)
         * 使用WEB-INF/classes目录 和 WEB-INF/lib目录之外的类。
         * 部署到%CATALINA_HOME%/common/lib目录下的jar文件的类文件是可用的
         */

        // 构造我们需要的类装入器

        ClassLoader commonLoader = null;
        ClassLoader catalinaLoader = null;
        ClassLoader sharedLoader = null;
        /**
         * 对于每个类载入器都会制定一条可以访问的路径,
         */
        try {

            File unpacked[] = new File[1];
            File packed[] = new File[1];
            File packed2[] = new File[2];
            ClassLoaderFactory.setDebug(debug);

            unpacked[0] = new File(getCatalinaHome(), "common" + File.separator + "classes");
            packed2[0] = new File(getCatalinaHome(), "common" + File.separator + "endorsed");
            packed2[1] = new File(getCatalinaHome(), "common" + File.separator + "lib");
            /**
             * commonLoader 类载入器,载入 <code>%CATALINA_HOME%/common/lib</code>、
             * <code>%CATALINA_HOME%/common/classes</code>、
             * <code>%CATALINA_HOME%/common/endorsed</code>目录下的java类
             */
            commonLoader = ClassLoaderFactory.createClassLoader(unpacked, packed2, null);

            unpacked[0] = new File(getCatalinaHome(), "server" + File.separator + "classes");
            packed[0] = new File(getCatalinaHome(), "server" + File.separator + "lib");
            /**
             * catalinaLoaderb 类载入 负责 载入 运行 Catalina Servlet容器 所需要类,它可以载入
             * <code>%CATALINA_HOME%/server/classes</code>、
             * <code>%CATALINA_HOME%/server/lib</code> 以及 commonLoader
             * 类载入器可以载入的类
             */
            catalinaLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);

            unpacked[0] = new File(getCatalinaBase(), "shared" + File.separator + "classes");
            packed[0] = new File(getCatalinaBase(), "shared" + File.separator + "lib");
            /**
             * sharedLoader 类载入器可以载入
             * <code>%CATALINA_HOME%/shared/classes</code>、
             * <code>%CATALINA_HOME%/shared/lib</code> 以及 commonLoader
             * 类载入器可以载入的类。
             * 
             * 在Tomcat 中,每个Web应用程序中与Context 容器相关联的每个类载入器的 父类载入器都是 sharedLoader
             * 类载入器
             * 
             * sharedLoader 类载入器 并不能访问Catalina的内部类,或CLASSPATH环境变量指定的类路径中的类
             */
            sharedLoader = ClassLoaderFactory.createClassLoader(unpacked, packed, commonLoader);
        } catch (Throwable t) {

            log("Class loader creation threw exception", t);
            System.exit(1);

        }

        Thread.currentThread().setContextClassLoader(catalinaLoader);

在创建了三个类载入器之后,main方法会载入Catalina类并创建它的一个实例,然后再将其赋值给 startupInstance变量

Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
            Object startupInstance = startupClass.newInstance();

然后它调用setParentClassLoader方法,并将sharedLoader类载入器作为参数传入

1 String methodName = "setParentClassLoader";
2             Class paramTypes[] = new Class[1];
3             paramTypes[0] = Class.forName("java.lang.ClassLoader");
4             Object paramValues[] = new Object[1];
5             paramValues[0] = sharedLoader;
6             Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
7             method.invoke(startupInstance, paramValues);

最后main方法会调用Catalina对象的process方法

 1 // 调用Catalina的process
 2             if (debug >= 1)
 3                 log("Calling startup class process() method");
 4             methodName = "process";
 5             paramTypes = new Class[1];
 6             paramTypes[0] = args.getClass();
 7             paramValues = new Object[1];
 8             paramValues[0] = args;
 9             method = startupInstance.getClass().getMethod(methodName, paramTypes);
10             // 将参数args 传入Catalina对象的 process方法中
11             method.invoke(startupInstance, paramValues);

 

posted @ 2018-12-25 20:39  陈东的博客  阅读(933)  评论(0编辑  收藏  举报