什么是类加载器?
简单来说的话,是用于实现“类加载动作”的加载器
“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为“类加载器”(Class Loader)。
这里需要注意一个特点,对于任何一个类,都要根据这个类本身以及加载它的类加载器来共同确认其在Java虚拟机中的唯一性。
有些拗口,但是希望各位同学能多念几遍理解一下。
从另一个角度来说,如果两类的名字相等,即使来自同一个.class文件,但是所用的类加载器不是同一个,虚拟机就不会认为是同一个类,是两个独立的类。
为什么需要双亲委派机制?
上面说了,不同类加载器加载出来的类,一定不相同。
所以,为了保护java底层本身的类不被篡改(比如java.lang.Object、java.lang.String)。
无论是在各种程序中,各种类加载器环境中都保证被同一个类加载器进行加载。
就是用双亲委派机制,全都委派给处于模型最顶端的启动类加载器进行加载。
如下图:
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
其实代码也简单得很,如下:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出ClassNotFoundException
// 说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载时
// 再调用本身的findClass方法来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
破坏双亲委派机制
很好理解啊,加载类的时候不按照这个顺序APPClassLoader->Ext ClassLoader->BootStrap ClassLoader进行加载就算是打破了双亲委派机制呗。
比如tomcat,只要用过tomcat部署工程的都知道,我们是把项目打成war包,让在webapp文件夹下面,那么就意味着tomcat同时可以运行多个web程序。
那在多个项目里面就有可能存在相同名称和路径的类。为了防止他们不冲突,tomcat就给每个web应用都创建一个类加载器实例,WebAppClassLoader。
该类重写了loaderClass()方法,优先加载当前应用目录下的类,如果找不到了,再进入用双亲委派机制一层层寻找加载器。
这其实算是一个双亲委派机制的补丁。
还有一种情况,双亲委派机制本身的模型就是“越基础的类,越由上层加载器来加载”。 那么什么是“基础”类? 就是哪些总作为被继承、调用API存在的类。 但是,如果存在那种,既有基础类型,又要回调用户代码怎么办?
这里最典型的就是JDBC,JDBC定义类接口,但是实现类肯定是由各个厂商来实现的, 比如mysql。
我们使用jdbc的时候,通过DriverManager来获取connection链接的,明显DriverManager是基础类,由BootstrapClassLoader加载的。
但是在我们使用DriverManager.getConnection()的时候,获取的一定是数据库厂商的类,BootstrapClassLoader不可能加载到第三方厂商的类啊。
解决方案就是,DriverManager在初始化的时候获取「线程上下文加载器」(Thread Context ClassLoader),在获取connection的时候使用线程上下文加载器来加载connection,这里的线程上下文加载器实际上还是App ClassLoader。
所以在获取connection的时候先找ExtClassLoader和BootstrapClassLoader加载器,只不过这俩加载器肯定加载不到,最终还是由AppClassLoader来加载。
其实话说回来,这些也可以算作双亲委派机制的加强补丁,并不是“破坏”双亲委派机制。 仁者见仁智者见智吧