Tomcat的自定义类加载器
Tomcat中,实现了一套自定义的类加载器。这一小节使用目前应用比较广泛的Tomcat9(9.0.84)源码进行分析。
# 环境搭建:
可以直接运行代码文件夹下的tomcat源码,这个项目已经修改过源码了,但是为了保证将来版本更新后同学们还能搭建最新代码版本的环境,将搭建过程在这里记录一下。
1、打开tomcat找到9.0版本的源代码,下载:
2、使用Idea打开,整个项目没有使用Maven,为了方便进行项目管理。在项目中创建pom文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat</artifactId>
<name>tomcat</name>
<version>c</version>
<build>
<finalName>tomcat</finalName>
<sourceDirectory>java</sourceDirectory>
<!--<testSourceDirectory>test</testSourceDirectory>-->
<resources>
<resource>
<directory>java</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>test</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.8</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>jaxrpc</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.6.1</version>
</dependency>
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bndlib</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>4.0.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3、选择Application,添加应用。JDK选择JDK8:
4、添加JVM参数:
-Duser.language=en -Duser.region=US
5、将JDTCompiler.java中这一段内容报错删除,这里我已经删除了:
6、在StringManager.java中添加这段代码,强制用iso8859-1编码获取字符串。
try {
value = new String(value.getBytes("iso8859-1"),"utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
7、在ContextConfig.java文件中添加JSP初始化器:
context.addServletContainerInitializer(new JasperInitializer(),null);
8、将web项目放入webapps目录下,运行项目:
Tomcat类加载器整体结构图如下:
# common类加载器
common类加载器主要加载tomcat自身使用以及应用使用的jar包,默认配置在catalina.properties文件中。
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar"
debug调试common累类加载器初始化代码:
它是URLClassLoader类的子类对象,根据URL获取jar包中的class字节码文件。
# catalina类加载器
catalina类加载器主要加载tomcat自身使用的jar包,不让应用使用,默认配置在catalina.properties文件中。
server.loader= 默认配置为空,为空时catalina加载器和common加载器是同一个。
配置参数之前,catalina加载器其实就是common加载器:
配置catalina加载的路径:
这次就创建了一个新的Catalina类加载器,专门加载配置目录下的类:
# shared类加载器
shared类加载器主要加载应用使用的jar包,不让tomcat使用,默认配置在catalina.properties文件中。
shared.loader= 默认配置为空,为空时shared加载器和common加载器是同一个。
# ParallelWebappClassLoader类加载器
ParallelWebappClassLoader类加载器可以多线程并行加载应用中使用到的类,每个应用都拥有一个自己的该类加载器。
demo1项目的类加载器:
demo2的类加载器:
为什么每个应用会拥有一个独立的ParallelWebappClassLoader类加载器呢?
同一个类加载器,只能加载一个同名的类。两个应用中相同名称的类都必须要加载。
所以tomcat的做法是为每个应用创建一个web应用类加载器:
ParallelWebappClassLoader的执行流程:
默认这里打破了双亲委派机制,应用中的类如果没有加载过。会先从当前类加载器加载,然后再交给父类加载器通过双亲委派机制加载。
# JasperLoader类加载器
JasperLoader类加载器负责加载JSP文件编译出来的class字节码文件,为了实现热部署(不重启让修改的jsp生效),每一个jsp文件都由一个独立的JasperLoader负责加载。
访问一个JSP文件,会触发JasperLoader类加载器的创建:
修改JSP文件:
用Arthas看类加载器情况:
类加载器数量变多了。