Tomcat的类加载

转自:http://www.tuicool.com/articles/NrI7NrN

http://www.tuicool.com/articles/eQBJfyn  

1. 启动过程

不管是操作系统,还是应用程序,都要从无到有,有一个循序渐进的启动过程。Tomcat也不例外,也是一切从头开始。嵌入其它应用,通过代码启动在新版本的Tomcat中也是支持的,但本文这里已最常见的命令行启动为例开始介绍。

我们打开Tomcat的压缩包,里面有startup.sh脚本和为了支持Windows环境的.bat文件,Tomcat启停的奥义就在这里。startup.sh里有一句:

exec "$PRGDIR"/"$EXECUTABLE" start "$@"

其中的$EXECUTABLE是在上面赋值的catalina.sh,也就是说会去执行catalina.sh,而且紧随其后的参数是start,我们继续看catalina.sh这个脚本,里面有这么一行开启的段落:

elif [ "$1" = "start" ] ; then

最终的执行语句是:

eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
      -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
      -Dcatalina.base="\"$CATALINA_BASE\"" \
      -Dcatalina.home="\"$CATALINA_HOME\"" \
      -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
      org.apache.catalina.startup.Bootstrap "$@" start \
      >> "$CATALINA_OUT" 2>&1 "&"

而$_RUNJAVA就是系统环境下的的java,忽略掉中间的参数不管,我们可以看到启动的Java类是org.apache.catalina.startup.Bootstrap。

_RUNJAVA="$JRE_HOME"/bin/java

而这个org.apache.catalina.startup.Bootstrap类里面有一个main方法,这和我们简单直接写的一个HelloWorld类编译执行的原理是一样的。

这里,我们可以有一个结论,在读任何系统的源码时,都将有一个入口,把握住了入口就把握住了一切。而任何用java运行起来的应用最终也都会有一个public class和一个main,这是绝对的,虽然servlet开发不需关注,只要针对API编程即可,但即使这样最终还是通过servlet的container来满足这一点。

也就是这样,Tomcat启动了。

2. Bootstrap类

上文书说到org.apache.catalina.startup.Bootstrap  main方法得到执行。找到对应的tomcat源代码,我们看到其中可以分为2大块:静态对象daemon的创建和初始化、根据命令行参数对daemon调用对应的方法。

先看第一块,从daemon的声明我们可以看出其就是一个Bootstrap的对象,在其为null的时候需要新创建一个,并且执行init()方法,初始化后将其赋值给daemon。我们这里更进一步,看看Bootstrap的这个init()方法到底做了哪些事情。

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;
    }

而这个方法,又可以分为两大部分:

  • 前5条语句,用来初始化catalina类加载器相关环境的变量,然后初始化各个类加载器对象,包括commonLoader、sharedLoader,其中最重要的就是catalinaLoader,将其设置为Bootstrap的一个属性值。
  • 从第六条语句开始,使用前面已经构件好的catalinaLoader加载tomcat最核心的对象,那就是org.apache.catalina.startup.Catalina类的对象catalinaDaemon,并以反射的方式调用其setParentClassLoader方法,把sharedLoader作为参数传入。

我们看到这个过程中,Bootstrap类持有的几个对象(静态的daemon、非静态的catalinaDaemon、commonLoader、sharedLoader、catalinaLoader)都得到了创建和必要的初始化。

那么接下来我们看main方法的后半部分。比如刚刚启动,我们得到的命令行参数是start,那么会执行如下这段代码:

else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
            }

会分别调用Bootstrap类的daemon对象的setAwait()、load()和start()三个方法。这三个方法我们略微深入一些,发现是从Bootstrap的方法到Catalina方法的一个调用,而且中间都是用了反射方式。至于这3个方法最终具体做了什么,我们这里先不细说,后面会分块做详细整理,不过可以简单了解下,其中:

  • setAwait()是设置了Catalina对象的一个属性值,其作用是告诉服务器启动后保持运行状态,并开启特定端口监听后续发来的指令,直到收到SHUTDOWN指令,做关闭服务器处理。
  • load()则是加载和初始化。对整个Tomcat服务器相关的配置文件进行加载和解析处理,并对Tomcat的各个组件进行初始化配置操作。
  • start(),这个其实不用多说,就是正式启动Catalina,或者说启动了Tomcat服务器的核心工作。

以此为例,对于Bootstrap的其它命令,比如stop等,本文就不想详细叙述了,感兴趣的可以按照这个路子去查看源代码。

3. Tomcat类加载

跟其他主流的Java Web服务器一样,Tomcat也拥有不同的自定义类加载器,达到对各种资源库的控制。一般来说,Java Web服务器需要解决以下四个问题:

①   同一个Web服务器里,各个Web项目之间各自使用的Java类库要互相隔离。

②   同一个Web服务器里,各个Web项目之间可以提供共享的Java类库。

③   服务器为了不受Web项目的影响,应该使服务器的类库与应用程序的类库互相独立。

④   对于支持JSP的Web服务器,应该支持热插拔(hotswap)功能。

对于以上几个问题,如果单独使用一个类加载器明显是达不到效果的,必须根据实际使用若干个自定义类加载器。

下面以本书主要剖析的Tomcat7为例,看看它的类加载器是怎样定义的?如图2-4-3,启动类加载器、扩展类加载器、应用程序类加载器这三个类加载器数据JDK级别的加载器,他们是唯一的,我们一般不会对其做任何更改。接下来则是Tomcat的类加载器,在tomcat7中,最重要的一个类加载器是Common ClassLoader,它的父类加载器是ApplicationClassLoader,负责加载 $CATALINA_BASE/lib、  $CATALINA_HOME/lib两个目录下所有的.class跟.jar文件。而下面虚线框的两个类加载器有必要说明一下,如果在Tomcat5版本,这两个类加载器实例默认与Common ClassLoader实例不同,Common ClassLoader为他们的父类加载器。而在Tomcat7中,这两个实例变量也存在,只是catalina.properties配置文件没有对server.loader跟share.loader两项进行配置,所以在程序里,这两个类加载器实例就被赋值为CommonClassLoader实例,即一个tomcat实例其实就只有CommonClassLoader实例。如以下代码

private void initClassLoaders() {

try {

commonLoader = createClassLoader( "common" , null );

if ( commonLoader == null ) {

commonLoader = this .getClass().getClassLoader();

}

catalinaLoader = createClassLoader( "server" , commonLoader );

sharedLoader = createClassLoader( "shared" , commonLoader );

catch (Throwable t) {

handleThrowable (t);

log .error( "Class loader creation threwexception" , t);

System. exit (1);

}

}

首先创建一个commonLoader,再把commonLoader作为参数传进createClassLoader方法里,在这个方法里面会根据catalina.properties中的server.loader和share.loader属性是否为空进行判断是否另外创建新的类加载器,如果属性为空则把commonLoader直接赋值给catalinaLoader和sharedLoader。如果默认配置满足不了你的需求,可以通过修改catalina.properties配置文件满足需要。WebAppClassLoader从名字来看就大概知道主要用于加载Web应用程序的,它的父类加载器是Common ClassLoader,一般有多个WebApp类加载器实例,每个类加载器加载一个Web程序,加载路径为/WebApp/WEB-INF目录。最后,JSP ClassLoader则是负责加载jsp文件编译出来的class,WebApp ClassLoader为它的父类加载器,当Tomcat检测到jsp文件有改动时,会创建一个新的JSP ClassLoader并替换掉当前的JSP ClassLassLoader,对/WebApp/WEB-INF目录下的JSP进行加载。

图2-4-3 Tomcat7类加载器

对照这样的一个类加载器结构,看看上面Java Web服务器需要解决的问题是否解决。由于每个Web应用项目都有自己的WebApp ClassLoader,很好地使多个Web应用程序之间互相隔离;并且能有效使Tomcat不受Web应用程序影响;Common ClassLoader的存在使多个Web应用程序能够互相共享类库;而每一个JSP文件对应一个Jsp ClassLoader则可以使Tomcat支持热替换功能。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值