1、類加載器定義html
從做用的角度來認識類加載器:(根據API文檔)java
1.類加載器是一個負責加載類的對象,而ClassLoader是一個抽象類。類加載器將xxx.class文件加載到JVM,生成xxx的Class對象。(Class詳見反射 點擊這裏)web
2.類加載器被安全管理器用來表示安全範圍。數組
2、類加載器種類及工做原理安全
主要有三 :服務器
1.Bootstrap Loader(引導加載器):負責加載%JAVA_HOME%/jre/lib下的核心jaride
2.Extended Loader(擴展加載器):負責加載%JAVA_HOME%/jre/lib/ext下的擴展jarthis
3.AppClass Loader(應用加載器):負責加載該應用程序CLASSPATH下的jar和.class文件spa
工做原理:code
每個類加載器都有一個與之關聯的上級類加載器,類加載器加載器類時使用委託機制。當須要加載一個類時,它首先將該任務委託給上級加載器,若是上級加載器沒能加載到,而後再自行加載,這是一個遞歸的過程。應用加載器的上級是擴展加載器,擴展加載器的上級是引導加載器,而引導加載無上級加載器。
3、類加載器的安全管理
JVM爲每一個類加載器維護了一個名字空間,每一個名字空間只能加載一個同名的類,不一樣名字空間可加載同名的類。在JVM中,同一個名字空間下的類能夠直接交互,不一樣則不能夠。若是a加載器和b加載器都加載了A類,那麼a加載器下的類要訪問A類,那麼必定是訪問的a加載器下的A類。這樣,經過類加載器就能夠進行安全管理。
舉個例子:咱們本身寫了一個類叫java.lang.String和類庫裏的String的全限定名如出一轍。如今在代碼中出現了String類,須要加載,過程以下。
>首先這個任務是應用加載器的,根據委託機制,應用加載器將任務委託給上級擴展加載器。
>擴展加載器接收到該任務,將任務委託給上級引導加載器。
>引導加載器加載在覈心類庫中尋找java.lang.String,找到了,加載,生成Class對象。
>引導加載器將結果返回給擴展加載器。
>擴展加載器將結果返回給應用加載器。
>應用加載器獲得結果。
應用加載器的到的結果是引導加載器從核心類庫中加載的java.lang.String 並無加載到咱們本身寫的String。這樣就保證了安全性。java其餘不少類都依賴String 類,若是加載了咱們本身寫的String就會形成嚴重的問題。類加載器的委託機制就提供了很好的安全防禦。
在例如:咱們寫了一個類叫java.lang.Hello,java核心類庫中並不存在這個類,但它的所屬的包結構倒是java.lang,而同一個包下的類具備訪問protected成員的權限,(protected詳細解析 點擊這裏)這樣就又會形成一些安全問題。分析一下類加載器加載Hello類的過程,按照上面的委託機制能夠肯定,引導、擴展加載器範圍內並無這個類,最後Hello是被應用加載器加載的,那麼這個類是在應用加載器的名字空間下。開頭已經給出,不一樣名字空間下的類不能直接交互,那麼此處的java.lang.Hello並不能像核心類庫的java.lang包下的類同樣擁有protected的訪問權限。類加載器的委託機制再一次提供了安全保護。
4、自定義類加載器
經過反射加載一個不存在的類
@Test public void funxx() throws ClassNotFoundException { Class.forName("xxx"); }
異常棧:
loaderClass源碼:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); 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 } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
分析以上信息,能夠發現,ClassLoader加載類是經過loadClass()方法完成的,其工做流程以下:
1.調用findLoadedClass()查看該類是否被加載過,若是沒有,返回null
2.若是findLoadedClass()返回null,那麼使用委託機制進行類加載。
3.若是getParent().loadClass() != null , 代表加載成功,不然調用本類的findClass()方法進行加載。
因而可知,咱們自定義一個類加載器,只須要繼承ClassLoader,並重寫其findClass()方法便可。
下面是我的寫的一個ClassLoader
package cn.edu; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class CustomizedClassLoader extends ClassLoader{ private String classpath; public CustomizedClassLoader() { } public CustomizedClassLoader(String classpath) { this.classpath = classpath; } //當loaderClass沒有找到該類時,會自動調用此方法尋找類。 @Override public Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] data = this.getBytesData(name); //獲取.class文件的字節數組 if(data == null) { throw new ClassNotFoundException("找不到類:" + name); } return this.defineClass(name, data, 0, data.length); //loaderClass繼承的方法,該方法經過字節數組生成Class對象 }catch(IOException e){ throw new ClassNotFoundException("找不到類:" + name); } } //讀取.class文件,返回字節數組,若讀取到的內容爲空,則返回null private byte[] getBytesData(String name) throws IOException{ name = name.replace(".", "//") + ".class"; //解析binary name,獲得文件絕對路徑 File file = new File(classpath,name); byte[] res = this.readFile(file); if(res.length == 0) { //字節數組爲空,返回null return null; } return res; } //讀取指定文件,返回字節數組,若找不到該文件,則IO異常 private byte[] readFile(File file) throws IOException{ FileReader reader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(reader); String str = null; String res = ""; while( (str = bufferedReader.readLine()) != null) { res += str; } return res.getBytes(); } }
五、Tomcat的類加載器
Tomcat有兩類類加載器,一類是服務器類加載器,用於加載${CATALINA_HOME}/lib目錄下的jar,這些是web容器所依賴的jar。還有一類是應用類加載器,加載${CONTEXT_PATH}/WEB-INF下的lib和class目錄,用於加載該應用所依賴的jar和class。這兩種類加載器,不適用委託機制,在應用須要加載類時,首先由應用加載器加載,加載失敗再由服務器加載器加載,而且class目錄優先於lib目錄。而且每個應用都有一個對應的類加載器加載。因此在給項目導入jar時應該注意不能導入與服務器類庫有衝突的jar。
本文我的編寫,水平有限,若有錯誤,懇請指出,歡迎討論分享。