zoukankan      html  css  js  c++  java
  • 启动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);
  • 相关阅读:
    apache伪静态设置
    ZeroClipboard.js兼容各种浏览器复制到剪切板上
    table 如何给tr border颜色
    JSON用法之将PHP数组转JS数组,JS如何接收PHP数组
    jquery操作select(增加,删除,清空)
    JS生成随机的由字母数字组合的字符串
    Redis连接(二)
    Redis集群(一)
    wap启用宏
    windows 10激活
  • 原文地址:https://www.cnblogs.com/ChenD/p/10171354.html
Copyright © 2011-2022 走看看