ClassLoader 類加載器模型
一、 類加載器
ClassLoader即常說的類加載器,其功能是用于從Class文件加載所需的類,主要場景用于熱部署、代碼熱替換等場景。
系統(tǒng)提供3種類加載器:Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader
1.1 Bootstrap ClassLoader
啟動類加載器,一般由C++實現(xiàn),是虛擬機的一部分。該類加載器主要職責是將JAVA_HOME路徑下的\lib目錄中能被虛擬機識別的類庫(比如rt.jar)加載到虛擬機內存中。Java程序無法直接引用該類加載器
1.2 Extension ClassLoader
擴展類加載器,由Java實現(xiàn),獨立于虛擬機的外部。該類加載器主要職責將JAVA_HOME路徑下的\lib\ext目錄中的所有類庫,開發(fā)者可直接使用擴展類加載器。該加載器是由sun.misc.Launcher$ExtClassLoader實現(xiàn)。
1.3 Application ClassLoader
應用程序類加載器,該加載器是由sun.misc.Launcher$AppClassLoader實現(xiàn),該類加載器負責加載用戶類路徑上所指定的類庫。開發(fā)者可通過ClassLoader.getSystemClassLoader()方法直接獲取,故又稱為系統(tǒng)類加載器。當應用程序沒有自定義類加載器時,默認采用該類加載器。
ClassLoader.java
1public?static?ClassLoader?getSystemClassLoader()?{
2????initSystemClassLoader();?//初始化系統(tǒng)類加載器?【見下文】
3????if?(scl?==?null)?{
4????????return?null;
5????}
6????SecurityManager?sm?=?System.getSecurityManager();
7????if?(sm?!=?null)?{
8????????ClassLoader?ccl?=?getCallerClassLoader();
9????????if?(ccl?!=?null?&&?ccl?!=?scl?&&?!scl.isAncestor(ccl))?{
10????????????sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
11????????}
12????}
13????return?scl;
14}
系統(tǒng)類加載器初始化
1private?static?synchronized?void?initSystemClassLoader()?{
2????if?(!sclSet)?{
3????????if?(scl?!=?null)
4????????????throw?new?IllegalStateException("recursive?invocation");
5????????sun.misc.Launcher?l?=?sun.misc.Launcher.getLauncher();
6????????if?(l?!=?null)?{
7????????????Throwable?oops?=?null;
8????????????scl?=?l.getClassLoader();
9????????????try?{
10????????????????scl?=?AccessController.doPrivileged(
11????????????????????new?SystemClassLoaderAction(scl));
12????????????}?catch?(PrivilegedActionException?pae)?{
13????????????????oops?=?pae.getCause();
14????????????????if?(oops?instanceof?InvocationTargetException)?{
15????????????????????oops?=?oops.getCause();
16????????????????}
17????????????}
18????????????if?(oops?!=?null)?{
19????????????????if?(oops?instanceof?Error)?{
20????????????????????throw?(Error)?oops;
21????????????????}?else?{
22????????????????????throw?new?Error(oops);
23????????????????}
24????????????}
25????????}
26????????sclSet?=?true;
27????}
28}二、雙親委派模型
ClassLoader的雙親委派模型中,各個ClassLoader之間的關系是通過組合關系來復用父加載器。當一個ClassLoader收到來自類加載的請求,首先把該請求委派該父類ClassLoader處理,當父類ClassLoader無法處理時,才由當前類ClassLoader來處理。對于每個ClassLoader這個方式,也就是父類的優(yōu)先于子類處理類加載的請求,那么也就是說任何一個請求第一次處理的便是最頂層的Bootstrap ClassLoader(啟動類加載器)。
類加載器的層級查找順序依次為:啟動類加載器,擴展類加載器,系統(tǒng)類加載器。系統(tǒng)類加載器是默認的應用程序類加載器。

這樣的好處是不同層次的類加載器具有不同優(yōu)先級,比如所有Java對象的超級父類java.lang.Object,位于rt.jar,無論哪個類加載器加載該類,最終都是由啟動類加載器進行加載,保證安全。即使用戶自己編寫一個java.lang.Object類并放入程序中,雖能正常編譯,但不會被加載運行,保證不會出現(xiàn)混亂。那么有人會繼續(xù)追問,如果自己再自定義一個類加載器來加載自己定義的java.lang.Object類呢? 這樣做也是不會成功的,虛擬機將會拋出一異常。
1protected?Class>?loadClass(String?name,?boolean?resolve)
2????throws?ClassNotFoundException
3{
4????synchronized?(getClassLoadingLock(name))?{
5????????//檢查該類是否已經(jīng)加載過
6????????Class?c?=?findLoadedClass(name);
7????????if?(c?==?null)?{
8????????????//如果該類沒有加載,則進入該分支
9????????????long?t0?=?System.nanoTime();
10????????????try?{
11????????????????if?(parent?!=?null)?{
12????????????????????//當父類的加載器不為空,則通過父類的loadClass來加載該類
13????????????????????c?=?parent.loadClass(name,?false);
14????????????????}?else?{
15????????????????????//當父類的加載器為空,則調用啟動類加載器來加載該類
16????????????????????c?=?findBootstrapClassOrNull(name);
17????????????????}
18????????????}?catch?(ClassNotFoundException?e)?{
19????????????????//非空父類的類加載器無法找到相應的類,則拋出異常
20????????????}
21
22????????????if?(c?==?null)?{
23????????????????//當父類加載器無法加載時,則調用findClass方法來加載該類
24????????????????long?t1?=?System.nanoTime();
25????????????????c?=?findClass(name);?//用戶可通過覆寫該方法,來自定義類加載器
26
27????????????????//用于統(tǒng)計類加載器相關的信息
28????????????????sun.misc.PerfCounter.getParentDelegationTime().addTime(t1?-?t0);
29????????????????sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
30????????????????sun.misc.PerfCounter.getFindClasses().increment();
31????????????}
32????????}
33????????if?(resolve)?{
34????????????//對類進行l(wèi)ink操作
35????????????resolveClass(c);
36????????}
37????????return?c;
38????}
39}當開發(fā)者需要自定義類加載器時,可通過覆寫loadClass()方法或者findClass()。
三、自定義類加載器
每一個ClassLoader都擁有自己獨立的類名稱空間,類是由ClassLoader將其加載到Java虛擬機中,故類是由加載它的ClassLoader和該類本身一起確定其在Java 運行時環(huán)境的唯一性。故只有同一個ClassLoader加載的同一個類,才能算是Java 運行時環(huán)境中的相同的兩個類。哪怕是來自同一個Class文件,即使被同一個虛擬機加載的兩個類,只要ClassLoader不同,那么也屬于不同的類。對于equals()、isinstanceof()等方法來判斷對象的相等或所屬關系都是需要基于同一個ClassLoader。
自定義類加載器示例
1package?com.yuanhh.classloader;
2
3import?java.io.IOException;
4import?java.io.InputStream;
5
6public?class?ClassLoadDemo{
7
8????public?static?void?main(String[]?args)?throws?Exception?{
9
10????????ClassLoader?clazzLoader?=?new?ClassLoader()?{
11????????????@Override
12????????????public?Class>?loadClass(String?name)?throws?ClassNotFoundException?{
13????????????????try?{
14????????????????????String?clazzName?=?name.substring(name.lastIndexOf(".")?+?1)?+?".class";
15
16????????????????????InputStream?is?=?getClass().getResourceAsStream(clazzName);
17????????????????????if?(is?==?null)?{
18????????????????????????return?super.loadClass(name);
19????????????????????}
20????????????????????byte[]?b?=?new?byte[is.available()];
21????????????????????is.read(b);
22????????????????????return?defineClass(name,?b,?0,?b.length);
23????????????????}?catch?(IOException?e)?{
24????????????????????throw?new?ClassNotFoundException(name);
25????????????????}
26????????????}
27????????};
28
29????????String?currentClass?=?"com.yuanhh.classloader.ClassLoadDemo";
30????????Class>?clazz?=?clazzLoader.loadClass(currentClass);
31????????Object?obj?=?clazz.newInstance();
32
33????????System.out.println(obj.getClass());
34????????System.out.println(obj?instanceof?com.yuanhh.classloader.ClassLoadDemo);
35????}
36}上面代碼的輸出結果
1class?com.yuanhh.classloader.ClassLoadDemo
2false輸出結果的第一行,可以看出這個對象的確是com.yuanhh.classloader.ClassLoadDemo實例化的對象;但第二句是false,這是由于代碼中的obj是由用戶自定義的類加載器clazzLoader來加載的,可通過obj.getClass().getClassLoader()獲取該對象的類加載器為com.yuanhh.classloader.ClassLoadDemo$xxx,而虛擬機本身會由系統(tǒng)類加載器加載的類ClassLoadDemo,可通過ClassLoadDemo.class.getClassLoader()得其類加載器為sun.misc.Launcher$AppClassLoader@XXX。所以可得出結論:即使都是來自同一個Class文件,加載器不同,仍然是兩個不同的類,所以返回值是false。
通過Class.forName()方法加載的類,采用的是系統(tǒng)類加載器。
四、 經(jīng)典應用場景
Tomcat,類加載器架構,自己定義了多個類加載器,
保證了同一個服務器的兩個Web應用程序的Java類庫隔離;
保證了同一個服務器的兩個Web應用程序的Java類庫又可以相互共享;比如多個Spring組織的應用程序不能共享,會造成資源浪費;
保證了服務器盡可能保證自身的安全不受不受部署Web應用程序影響;
支持JSP應用的服務器,大多需要支持熱替換(HotSwap)功能。
OSGi(Open Service GateWay Initiative),是基于Java語言的動態(tài)模塊化規(guī)范。已成為Java世界的“事實上”的模塊化標準,最為熟悉的案例的Eclipse IDE。
source: //yuanfentiank789.github.io/2016/01/24/java-classloader

喜歡,在看
