Tomcat 类加载器的实现方法及实例代码

2019-10-18 20:05:11王冬梅

Tomcat 内部定义了多个 ClassLoader,以便应用和容器访问不同存储库中的类和资源,同时达到应用间类隔离的目的。

1. Java 类加载机制

类加载就是把编译生成的 class 文件,加载到 JVM 内存中(永久代/元空间)。

类加载器之所以能实现类隔离,是因为两个类相等的前提是它们由同一个类加载器加载,否则必定不相等。

JVM 在加载时,采用的是一种双亲委托机制,当类加载器要加载一个类时,加载顺序是:

首先将请求委托给父加载器,如果父加载器找不到要加载的类然后再查找自己的存储库尝试加载

这个机制的好处就是能够保证核心类库不被覆盖。

而按照 Servlet 规范的建议,Webapp 加载器略有不同,它首先会在自己的资源库中搜索,而不是向上委托,打破了标准的委托机制,来看下 Tomcat 的设计和实现。

2. Tomcat 类加载器设计

Tomcat 整体类加载器结构如下:

其中 JDK 内部提供的类加载器分别是:

Bootstrap - 启动类加载器,属于 JVM 的一部分,加载 <JAVA_HOME>/lib/ 目录下特定的文件Extension - 扩展类加载器,加载 <JAVA_HOME>/lib/ext/ 目录下的类库Application - 应用程序类加载器,也叫系统类加载器,加载 CLASSPATH 指定的类库

Tomcat 自定义实现的类加载器分别是:

Common - 父加载器是 AppClassLoader,默认加载 ${catalina.home}/lib/ 目录下的类库Catalina - 父加载器是 Common 类加载器,加载 catalina.properties 配置文件中 server.loader 配置的资源,一般是 Tomcat 内部使用的资源Shared - 父加载器是 Common 类加载器,加载 catalina.properties 配置文件中 shared.loader 配置的资源,一般是所有 Web 应用共享的资源WebappX - 父加载器是 Shared 加载器,加载 /WEB-INF/classes 的 class 和 /WEB-INF/lib/ 中的 jar 包JasperLoader - 父加载器是 Webapp 加载器,加载 work 目录应用编译 JSP 生成的 class 文件

在实现时,上图不是继承关系,而是通过组合体现父子关系。Tomcat 类加载器的源码类图:

Common、Catalina 、Shared 它们都是 StandardClassLoader 的实例,在默认情况下,它们引用的是同一个对象。其中 StandardClassLoader 与 URLClassLoader 没有区别;WebappClassLoader 则按规范实现以下顺序的查找并加载:

从 JVM 内部的 Bootstrap 仓库加载从应用程序加载器路径,即 CLASSPATH 下加载从 Web 程序内的 /WEB-INF/classes 目录从 Web 程序内的 /WEB-INF/lib 中的 jar 文件从容器 Common 加载器仓库,即所有 Web 程序共享的资源加载

接下来看下源码实现。

3. 自定义加载器的初始化