雙親委託模型
一、什麼是雙親委託
雙親委派模型,就是如果一個類加載器收到了類加載請求,它並不會自己先去加載,而是把這個請求委託給父類的加載器去執行,如果父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸,請求最終將到達頂層的啓動類加載器,如果父類加載器可以完成類加載任務,就成功返回,倘若父類加載器無法完成此加載任務,子加載器纔會嘗試自己去加載,這就是雙親委派模型
二、源碼分析
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);
}