Servlet笔记——(1.2.2)Tomcat类加载机制

1.2.2 Tomcat类加载机制(了解)

1.2.2.1 JVM类加载机制

JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构。其中引导类、扩展类、系统类三个加载器是JVM内置的。它们的作用分别是:

(1) 引导类加载器:使用native代码实现,在rt.jar等包中搜索运行JVM所需的类,例如java.lang等包下的类。

(2) 扩展类加载器:负责载入标准扩展目录中的类,例如Sun的JVM的扩展目录是/jdk/jre/lib/ext。

(3) 系统类加载器:默认的类加载器,搜索环境变量CLASSPATH中指明的路径。

Servlet笔记——(1.2.2)Tomcat类加载机制


1.2.2.2 双亲委派模型

既然类加载器是树形结构,那加载类时就需要定义类到底由当前加载器还是父加载器去搜索加载。

JVM加载模型的工作过程是:

  如果一个类加载器收到了类加载的请求,它不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。

  每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的引导类加载器中。

   只有父类加载无法完成这个请求时,子类加载器才会尝试自己去加载。


Servlet笔记——(1.2.2)Tomcat类加载机制

为什么要让父类加载器优先去加载呢?试想如果子类加载器先加载,那么我们可以写一些与java.lang包中基础类同名的类,然后再定义一个子类加载器,这样整个应用使用的基础类就都变成我们自己定义的类了。这样就有很大的安全隐患!

所以自己编写类加载器时,如果没有特殊原因,一定要遵守类加载的双亲委派模型。

1.2.2.3 Tomcat类加载器

除了Java环境自身的三层类加载器,前面提到的,在Tomcat中主要有commonLoader、catalinaLoader、sharedLoader这几个类加载器。细看源码,实际上这几个loader都是Tomcat中的org.apache.catalina.loader.StandardClassLoader类。StandardClassLoader直接继承于java.net中的URLClassLoader类,最终继承于java.lang.ClassLoader类。

而除了这三个loader外,Tomcat中还有个关键的ClassLoader,也在这里一起介绍,那就是org.apache.catalina.loader.WebappClassLoader类。在实际的Tomcat实例中,会由多个WebappClassLoader类对象,就像其名字说描述的,一个Web APP,就有一个这样的loader

对于一般的Java应用开发来讲,我们其实并不需要太过关注JVM所提供的运行环境,不必关注ClassLoader。但在Tomcat中,为了提高系统的灵活性,引入了commonLoader、sharedLoader、catalinaLoader;为了支持和分隔多个web应用,使用了WebappClassLoader

(1) Tomcat中的系统类加载器。Tomcat也是一个Java应用,他也是在最初系统提供的几层类加载器环境下运行起来的。那么Tomcat的一些最基本的类,也和其它简单Java应用一样,是通过系统的类加载器来加载的,比如默认配置下的tomcat/bin目录下的bootstrap.jar、tomcat-juli.jar、commons-daemon.jar这几个jar包中的类。

(2) Tomcat的Common Loader。Common Loader是Tomcat在系统类加载器之上建立起来的,其父loader是系统类加载器。Common Loader负责上面几个jar包外的Tomcat的大部分java类,通常情况下是tomcat/lib下的所有jar包。

(3) Webapp Class Loader。这个类加载器可以说是Tomcat种最重要的Class Loader,它创造了各个Web app空间。在实现上,它打破了系统默认规则,或者说是打破了java.lang.ClassLoader逻辑中的“双亲委派”模式,提供了一套自定义的类加载流程。默认情况下,对于一个未加载过的类,WebappClassLoader会先让系统加载java.lang.Object等Java本身的基础类,如果不是基础类则优先在当前Web app范围内查找并加载,如果没加载到,再交给common loader走标准的双亲委派模式加载。

对于上面这三点做一些说明:

在tomcat/conf目录的catalina.properties中有common.loader、server.loader、shared.loader的配置,这分别对应着commonLoader、catalinaLoader和sharedLoader。我们可以看到,默认情况下,serverl.loader和shared.loader的配置是空的,这意味着此两者在运行时和commonLoader相同。实际上,在Tomcat5.5之后的版本中,做了简化,只有commonLoader具备实际意义。而在5.5及之前的版本中,三者各不相同,各有分工。可参看官方文档作对比: Tomcat-5.5-ClassLoader 、 Tomcat-7.0-ClassLoader 。

综上所述:

Tomcat基本遵守了JVM的委派模型,但也在自定义的类加载器中做了细微的调整,以适应Tomcat自身的要求。在Tomcat 6中默认情况下,不是完全按照先Tomcat的lib再Web应用的lib这种顺序去加载类。Jar包的加载顺序是:

(1) JRE中的Java基础包

(2) Web应用WEB-INF\lib下的包

(3) Tomcat\lib下的包

如果想要在Web应用间共享一些Jar包,则不仅需要将公共包放在Tomcat的lib下,还要删掉Web应用lib中的包,否则Tomcat启动时还是会优先加载Web应用lib下的包的。