tomcat work启动过程(二)

Tomcat 的运行时视图,简单地看,其实就是一些相互关联的组件。这些组件相互协作,完成一定的任务(比如部署Web 应用、处理到HTTP 请求等)。Tomcat 启动过程中所做的主要工作,也就是创建这些组件,并建立组件之间的关联。当然,要创建哪些组件,组件之间怎么关联,这是根据配置文件来定制的。

服务器程序的启动过程一般都有“三段式”,Tomcat 也不例外,它的三段式分别是initloadstart

init 方法

我们先看看Bootstrapinit 方法。

Java代码 复制代码
  1. public void init() throws Exception   
  2.     {   
  3.   
  4.         // Set Catalina path   
  5.         setCatalinaHome();   
  6.         setCatalinaBase();   
  7.   
  8.         initClassLoaders();   
  9.   
  10.         Thread.currentThread().setContextClassLoader(catalinaLoader);   
  11.   
  12.         SecurityClassLoad.securityClassLoad(catalinaLoader);   
  13.   
  14.         // Load our startup class and call its process() method   
  15.         if (log.isDebugEnabled())   
  16.             log.debug("Loading startup class");   
  17.         Class startupClass =   
  18.             catalinaLoader.loadClass   
  19.             ("org.apache.catalina.startup.Catalina");   
  20.         Object startupInstance = startupClass.newInstance();   
  21.   
  22.         // Set the shared extensions class loader   
  23.         if (log.isDebugEnabled())   
  24.             log.debug("Setting startup class properties");   
  25.         String methodName = "setParentClassLoader";   
  26.         Class paramTypes[] = new Class[1];   
  27.         paramTypes[0] = Class.forName("java.lang.ClassLoader");   
  28.         Object paramValues[] = new Object[1];   
  29.         paramValues[0] = sharedLoader;   
  30.         Method method =   
  31.             startupInstance.getClass().getMethod(methodName, paramTypes);   
  32.         method.invoke(startupInstance, paramValues);   
  33.   
  34.         catalinaDaemon = startupInstance;   
  35.     }  
public void init() throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        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;
    }

 
该方法的主要工作依次是:

  1. 设置CatalinaTomcat Servlet 容器的代号)的路径:CATALINA_HOMECATALINA_BASE
  2. 初始化Tomcat 的类加载器体系
  3. 创建org.apache.catalina.startup.Catalina 对象(启动阶段剩余的工作由Catalina类 完成)

Catalina_Home 和Catalina_Base

首先,我们看看这两个路径有何区别。Tomcat的启动脚本已经设置了CATALINA_HOMECATALINA_BASE 的值,而且两者的值是相同的,都是Tomcat的根目录。那么为什么还要设置这两个变量呢?

我们可以从Tomcat 5.5 的配置文档(http://tomcat.apache.org/tomcat-5.5-doc/config/host.html )中找到答案:

The description below uses the variable name $CATALINA_HOME to refer to the directory into which you have installed Tomcat 5, and is the base directory against which most relative paths are resolved. However, if you have configured Tomcat 5 for multiple instances by setting a CATALINA_BASE directory, you should use $CATALINA_BASE instead of $CATALINA_HOME for each of these references.

从这段描述可以看出CATALINA_HOMECATALINA_BASE 的区别。简单的说,CATALINA_HOMETomcat 的安装目录,CATALINA_BASETomcat 的工作目录。如果我们想要运行Tomcat 的多个实例,但是不想安装多个Tomcat 软件副本。那么我们可以配置多个工作目录,每个运行实例独占一个工作目录,但是共享同一个安装目录。


Tomcat 每个运行实例需要使用自己的conflogstempwebappsworkshared 目录,因此CATALINA_BASE 就指向这些目录。 而其他目录主要包括了Tomcat 的二进制文件和脚本,CATALINA_HOME 就指向这些目录。

如果我们希望再运行另一个Tomcat 实例,那么我们可以建立一个目录,把conflogstempwebappsworkshared 拷贝到该目录下,然后让CATALINA_BASE 指向该目录即可。

下面,我们看看Bootstrap 是如何设置CATALINA_HOME和CATALINA_BASE。

Java代码 复制代码
  1. private void setCatalinaHome() {   
  2.         if (System.getProperty("catalina.home") != null)   
  3.             return;   
  4.         File bootstrapJar =   
  5.             new File(System.getProperty("user.dir"), "bootstrap.jar");   
  6.         if (bootstrapJar.exists()) {   
  7.             try {   
  8.                 System.setProperty   
  9.                     ("catalina.home",   
  10.                      (new File(System.getProperty("user.dir"), ".."))   
  11.                      .getCanonicalPath());   
  12.             } catch (Exception e) {   
  13.                 // Ignore   
  14.                 System.setProperty("catalina.home",   
  15.                                    System.getProperty("user.dir"));   
  16.             }   
  17.         } else {   
  18.             System.setProperty("catalina.home",   
  19.                                System.getProperty("user.dir"));   
  20.         }   
  21.     }  
private void setCatalinaHome() {
        if (System.getProperty("catalina.home") != null)
            return;
        File bootstrapJar =
            new File(System.getProperty("user.dir"), "bootstrap.jar");
        if (bootstrapJar.exists()) {
            try {
                System.setProperty
                    ("catalina.home",
                     (new File(System.getProperty("user.dir"), ".."))
                     .getCanonicalPath());
            } catch (Exception e) {
                // Ignore
                System.setProperty("catalina.home",
                                   System.getProperty("user.dir"));
            }
        } else {
            System.setProperty("catalina.home",
                               System.getProperty("user.dir"));
        }
    }

 
CATALINA_HOME 保存在系统变量catalina.home 中。setCatalinaHome 方法首先检查catalina.home 系统变量是否设置。如果已经设置,则直接返回;否则,就检查Tomcat 的启动目录(系统变量user.dir)。如果启动目录是bin,那么启动目录下就存在bootstrap.jar故CATALINA_HOME 就是bin 的上级目录;如果启动目录下没有bootstrap.jar ,那么就假定启动目录就是CATALINA_HOME

Java代码 复制代码
  1. private void setCatalinaBase() {   
  2.     if (System.getProperty("catalina.base") != null)   
  3.         return;   
  4.     if (System.getProperty("catalina.home") != null)   
  5.         System.setProperty("catalina.base",   
  6.                            System.getProperty("catalina.home"));   
  7.     else  
  8.         System.setProperty("catalina.base",   
  9.                            System.getProperty("user.dir"));   
  10. }  
    private void setCatalinaBase() {
        if (System.getProperty("catalina.base") != null)
            return;
        if (System.getProperty("catalina.home") != null)
            System.setProperty("catalina.base",
                               System.getProperty("catalina.home"));
        else
            System.setProperty("catalina.base",
                               System.getProperty("user.dir"));
    }

 
CATALINA_BASE 保存在系统变量catalina.base 中。setCatalinaBase 方法首先检查catalina.base 系统变量是否设置,如果已经设置,就直接返回。否则,就检查catalina.home 系统变量是否设置。如果已经设置,则以CATALINA_HOME 作为CATALINA_BASE 。否则,就以Tomcat 的启动目录(系统变量user.dir )作为CATALINA_BASE

catalina.bat 中已经设置了catalin.homecatalina.base 的值,详见下面代码:

Java代码 复制代码
  1. %_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%  
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

 
我们可以通过修改catalina.batCATALINA_HOMECATALINA_BASE 的值 ,来设置catalina.homecatalina.base 这两个系统变量。

初始化类加载器体系

initClassLoaders() 很显然是初始化类加载器。在看代码之前,我们先看看Tomcat 的类加载器体系。

Tomcat 的类加载器体系

很多服务器程序(TomcatJBossGlassFishGeronimo 等),都会有自己的类加载器体系。这主要是为了要把开发者编写的各种应用(WAREAR 等)部署到容器中,并实现应用之间的隔离。

Tomcat 也实现了自己的类加载器体系。这个在Tomcat 的官方文档中有详细介绍,详见http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html 。这里做点简单介绍。

Tomcat 的类加载器体系如下图所示:

     Bootstrap
         |
       System
         |
       Common
        / /
    Catalina Shared
              / /
        Webapp1 Webapp2 ...


BootStrap 就是JVM 的启动类加载器,负责加载Java 核心类库和系统扩展类库(%JAVA_HOME%/jre/lib/ext 下的jar 文件)。有的JVM实现提供了两个类加载器,分别加载核心类库和系统扩展类库。我们这里仍用一个Bootstrap 类加载器表示,不影响理解。

System 就是JVM的系统类加载器,负责加载CLASSPATH 下的jar 文件,这些文件包括:

  1. %CATALINA_HOME%/bin/bootstrap.jar
  2. %JAVA_HOME%/lib/tools.jar
  3. %CATALINA_HOME%/bin/commons-logging-api-x.y.z.jar
  4. %CATALINA_HOME%/bin/tomcat-juli.jar
  5. %CATALINA_HOME%/bin/tomcat-daemon.jar
  6. %CATALINA_HOME%/bin/jmx.jar (即之前提到的JDK 1.4 兼容包)


其中,12 是在catalina.bat 中指定的,3-6 是在bootstrap.jarMETA-INF/MANIFEST.MF 文件中指定的。

Common 就是公共类加载器,负责加载Tomcat 内部和Web 应用程序都可以看到的类。%CATALINA_HOME%/conf/catalina.properties 文件中指定了这些类:

common.loader=${catalina.home}/common/classes,${catalina.home}/common/i18n/*.jar,${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar

可见,Common 加载的是%CATALINA_HOME%/common 目录下的jar 文件。

Catalina 负责加载Tomcat 内部使用的类,这些类对于Web 应用程序不可见。同样,%CATALINA_HOME%/conf/catalina.properties 文件中指定了这些类:

server.loader=${catalina.home}/server/classes,${catalina.home}/server/lib/*.jar

可见,Catalina 加载的是%CATALINA_HOME%/server 目录下的jar 文件。

Shared 负责加载 在Web应用程序之间共享的类,这些类对于Tomcat 内部是不可见的。同样,%CATALINA_HOME%/conf/catalina.properties 文件中指定了这些类:

shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar

可见,Catalina 加载的是% CATALINA_BASE %/shared 目录下的jar 文件。注意,这里是CATALINA_BASE shared 属于Tomcat 的工作目录。

Webapp 负责加载Web 应用程序的类,这些类只对本Web 应用程序可见。很显然,每个Web 应用程序都有一个独立的Webapp 类加载器。它们加载的类包括WAR 包中/WEB-INF/classes/WEB-INF/lib 目录下的类。

需要注意的是,Webapp 并没有遵循类加载器委派模型。Webapp 优先从自己的搜索路径中加载类,而不是委派给父亲Shared 。这样做的原因应该是保证

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值