双亲委托模型
一、什么是双亲委托
双亲委派模型,就是如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模型
二、源码分析
Class aClass = ClassLoader.getSystemClassLoader().loadClass("MainActivity");
首先,我们先看下,像上述一个类(MainActivity)是如何通过反射被加载出来的
//public abstract class ClassLoader
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
//1.首先看该类是否应加载过,有则直接返回
Class<?> c = findLoadedClass(name);
if (c == null) {
//2.没有加载过,则调用parent.loadClass方法去父类中去加载
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//3.当父类中不存在时,则自己通过findClass方法去加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
//4.返回该对象
return c;
}
如图中的代码,整体的逻辑为,当我们根据类名加载一个类时,
1.首先看该类是否应加载过,有则直接返回
2.没有加载过,则调用parent.loadClass方法去父类中去加载
3.当父类中不存在时,则自己通过findClass方法去加载
4.返回该对象
三、理解
上述的代码,就是一个标准的双亲委托模型,就完了,就怎么多,怎么样,是不是感觉内容很少,和脑海中对他的幻想存在很多差别,现在,咱们就从上面的代码来看看这个模型的好处在哪里,优势是什么
举个例子,假设一个类加载器的结构是这样的AClassLoader extends BClassLoader,BClassLoader extends ClassLoader。当AClassLoader 中有一个Test.class类,BClassLoader中也有一个Test.Class类时,我们调用AClassLoader .loadClass(“Test”);时,加载的是哪个类呢?
根据上面的源码逻辑,当我们调用aClassLoader.loadClass方法时,先看该类是否加载过,在首次时没有加载过,则调用父bClassLoader的loadClass方法,b也因首次请求未加载过该类,则请求基类ClassLoader,ClassLoader中也没有,由于是根类,则通过该类的findclass方法去找,未找到,则通知bClassLoader未空的结果,b则根据空结果,去自身的findclass方法中去查找,找到了,则返回该B 中的Test.class。
四、优势
**1.这种层级关系可以避免重复加载:**Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
**2.在安全的角度,可以在一定程度上方式源码被替换:**假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
五.自定义类加载器
一个简单的自定义类加载器的方式如下
public class test extends ClassLoader{
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
//如果你想破坏双亲委托的模型,你可以重写这个方法.毕竟用户的需求是多种多样的,你开发出一个甜品店,保不齐有人非要过来买条狗。。
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//如果你想保持双亲委托的模型,请重写这个方法
return super.findClass(name);
}
}
原因则可以参照源码分析时的过程来理解(当父类中不存在时,则自己通过findClass方法去加载),并且,原生的注释也对这两个方法的用途解释的很明白了
/**
* Loads the class with the specified <a href="#name">binary name</a>.
* This method searches for classes in the same manner as the {@link
* #loadClass(String, boolean)} method. It is invoked by the Java virtual
* machine to resolve class references. Invoking this method is equivalent
* to invoking {@link #loadClass(String, boolean) <tt>loadClass(name,
* false)</tt>}.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class was not found
*/
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* Finds the class with the specified <a href="#name">binary name</a>.
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass <tt>loadClass</tt>} method after checking the
* parent class loader for the requested class. The default implementation
* throws a <tt>ClassNotFoundException</tt>.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}