Tomcat类加载器机制

  上一篇文章介绍了Tomcat的组件及架构设计理念,本章将来了解Tomcat的类加载器机制。
  类加载器是一切Java应用运行的基础,了解类加载器机制便于我们掌握它的运行边界,有助于运行异常时的快速定位。
1、J2SE标准类加载器
  JVM默认提供了三个类加载器,它们以父子树的方式创建,同时使用委派模式确保应用程序可以通过自身的类加载器(System)加载所有的可见Java类,如图。
Tomcat类加载器机制
  其中BootStrap用于加载JVM提供的基础运行类,即位于%JAVA_HOME%/jre/lib目录下的核心类库。
  Extension是Java提供的一个标准的扩展机制用于加载除核心类库以外的Jar包,因此,只要复制到指定的扩展目录下的Jar,JVM会自动加载,不需要通过-classpath指定。默认的扩展目录是%JAVA_HOME%/jre/lib/ext。典型的应用场景就是,Java使用该类加载器加载JVM默认提供的但不属于核心类库的Jar包,如JCE(Java加密扩展)等。不建议将应用程序依赖的类库放置到扩展目录下,因为该目录下的类库对所有基于该JVM运行的应用程序可见。
  System是用于加载环境变量CLASSPATH指定目录下或-classpath运行参数指定的Jar包,System类加载器通常用于加载应用程序Jar包及其启动入口类,如Tomcat的Bootstrap类由System类加载器加载。
  应用程序在不自己构造类加载器的情况下,使用System作为默认的类加载器。如果应用程序构造类加载器,基本也是以System作为父类加载器的。除了支持类加载器按照层级创建外,JVM还提供了一套称为Endorsed Standards Override Mechanism的机制用于允许替换JCP之外生成的API。通过这个机制,应用程序可以提供新版本的API来覆盖JVM的默认实现。(JVM默认的Endorsed目录为%JAVA_HOME%/lib/endorsed)。

2、Tomcat加载器
  应用服务器通常会自行创建类加载器以实现更灵活的控制,一方面是对规范的实现,因为Servlet规范要求每个Web应用都有一个独立的类加载器实例;另一方面也是架构的考虑。Web应用类加载器具有以下的特点:

  • 隔离性:Web应用类库相互隔离,避免依赖库或应用包相互影响。
  • 灵活性:正因为Web应用之间的类加载器相互独立,只能就一个Web应用进行重新部署,此时该Web应用的类加载器将会重新创建,而且不会影响其他的Web应用。
  • 高性能:由于每个Web应用都有一个类加载器,因此Web应用在加载类时,不会搜索其他的Web应用包含的Jar包,性能就会高于应用服务器只有一个类加载器的情况。
    Tomcat的类加载器方案如图。
    Tomcat类加载器机制
      除了每个Web应用的类加载器外,Tomcat提供了3个基础的类加载器和Web应用类加载器,且这3个类加载器指向的路径和包列表均可以由catalina.properties配置。
      其中Common以System为父类加载器,是位于Tomcat应用服务器顶层的公用类加载器,其路径为common.loader,默认指向CATALINA_HOME/lib下的包。
      Catalina以Common为父类加载器,是用于加载Tomcat应用服务器的类加载器,其路径为server.loader,默认是空。此时Tomcat使用Common类加载器加载应用服务器。
      Shared以Common为父类加载器,是所有Web应用的父加载器,其路径为sharde.loader,默认是空。此时Tomcat使用Common类加载器加载应用服务器。
      Web应用以Shared为父类加载器。加载WEB-INF/classes目录下的未压缩的CLass和资源文件、WEB-INF/lib下目录下的Jar包。该类加载器只对当前Web应用可见,对其他Web应用均不可见。
      默认情况下,这3个类加载器是同一个,但可以通过配置创建3个不同的类加载器,使其各司其职。
      Common类加载器负责加载Tomcat应用服务器内部和 Web应用均可见的的类,如Servlet规范相关包和一些通用的工具包。
      Catalina类加载器负责加载只有Tomcat应用服务器内部可见的类,这些类对Web应用不可见,如Tomcat的具体实现类。
      Shared类加载器负责加载Web应用共享的类,这个类Tomcat不会依赖。
      Tomcat服务器$CATALINA_HOME/lib目录下的包作为启动入口由System类加载器加载,通过将这几个启动包剥离,Tomcat简化了应用服务器的启动,增加的灵活性。

3、Web应用类加载器
  Java默认的类加载机制是委派模式,过程如下。
   (1)从缓存中加载。
   (2)如果缓存中没有,则从父类加载器中加载。
   (3)如果父类加载器没有,则从当前类加载器加载。
   (4)如还是没有,则抛出异常。
  Tomcat提供的Web应用类加载器与默认的委派模式稍有不同。当进行类加载时,除JVM基础类库外,它会首先尝试通过当前类加载器加载,然后再进行委派。所以,Web应用类加载器默认加载顺序如下。
   (1)从缓存中加载。
   (2)如没有,则从JVM的Bootstrap类加载器加载。
   (3)如没有,则从当前类加载器加载,按照WEB-INF/classes、WEB-INF/lib的顺序。
   (4)如没有,则从父类加载器加载,加载顺序为System、Common、Shared。
  Tomcat提供了delegate属性用于控制是否启用Java委派模式,默认为不启用(false)。除了可以通过delegate外,Tomcat还可以通过packageTriggerDeny属性只让某些路径采用Java2委派模式,Web应用类加载器对于符合packageTriggerDeny指定包路径的类强制采用Java的委派模式。
  Tomcat通过上述机制实现为Web应用中的Jar包覆盖服务器提供包的目的。