Tomcat如何打破双亲委派机制?
Tomcat是一个Web框架,并且可以支持部署多个web项目,web项目在Tomcat被抽象的称为Context,即每一个web项目是一个Context。而每个Context是独立的,比如项目A可以引用spring1.0,而项目B可以引用spring2.0版本,如果是双亲委派机制,那么只能存在一个spring版本。Tomcat是如何打破双亲委派机制的呢?
什么是类加载?
Java 的类加载,就是把字节码格式“.class”文件加载到 JVM 的方法区,并在 JVM 的堆区建立一个java.lang.Class
对象的实例,用来封装 Java 类相关的数据和方法。那 Class 对象又是什么呢?你可以把它理解成业务类的模板,JVM 根据这个模板来创建具体业务类对象实例。
JVM 并不是在启动时就把所有的“.class”文件都加载一遍,而是程序在运行过程中用到了这个类才去加载。JVM 类加载是由类加载器来完成的。
双亲委派机制
首先我们来复习一下双亲委派机制:
- BootstrapClassLoader 是启动类加载器,由 C 语言实现,用来加载 JVM 启动时所需要的核心类,比如
rt.jar
、resources.jar
等。 - ExtClassLoader 是扩展类加载器,用来加载
\jre\lib\ext
目录下 JAR 包。 - AppClassLoader 是系统类加载器,用来加载 classpath 下的类,应用程序默认用它来加载类。
- 自定义类加载器,用来加载自定义路径下的类。
简单描述下:
每个类加载器负责一部分指定类的加载。若遇到某个类,需要加载,并不直接去加载,先让父加载器去查询是否加载过,若父类加载器加载过,则返回结束,否则让父类加载器尝试加载,若不是当前父类负责的加载内容,则向下返回给子类,让子类自己尝试加载。
这样的好处:
- 避免重复加载(class的唯一性)
- 防止破坏已有的一些系统的class
而Tomca t每个Context可能会存在相同全限定类名,比如项目A中有个类叫com.add.domain.User
而项目B中也可能有个类叫com.add.domain.User
。这样的情况肯定是要将两个类都加载,并且要实现项目之间的独立。或者是项目A和B都依赖了一个spring,但是版本是不同的,这样也需要都加载,并且要实现项目之间的独立。
JVM类加载源码分析
现在Java如果不搞个源码分析,都找不到工作了
首先要明确,上面双亲委派机制体系中,不是继承的关系,而是父子关系,是用一个指针指向父类加载器实现的,具体是实现ClassLoader
类。我们来看看默认的双亲委派机制的类加载源码。
public abstract class ClassLoader {
// 每个类加载器都有个父加载器
private final ClassLoader parent;
public Class<?> loadClass(String name) {
// 查找一下这个类是不是已经加载过了
Class<?> c = findLoadedClass(name);
// 如果没有加载过
if( c == null ){
// 先委托给父加载器去加载,注意这是个递归调用
if (parent != null) {
c = parent.loadClass(name);
}else {
// 如果父加载器为空,查找 Bootstrap 加载器是不是加载过了