ClassLoader in Java
簡單寫寫Java反射和ClassLoader,之前玩過反射,覺得很有趣,這裡就很簡單的總結點,為學習Spring3.x做準備。 1.Java反射 在Jdbc中我們通常首先會根據一個字串載入特定資料庫驅動類的位元組碼,如下:
Class.forName("com.mysql.jdbc.Driver");Class.forName("oracle.jdbc.OracleDriver");Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
這裡其實是用到了類載入器ClassLoader:
// ClassLoader loader = ReflectTest.class.getClassLoader();ClassLoader loader = Thread.currentThread().getContextClassLoader();
然後可以進行一些操作了:
//擷取聲明的構造方法(任何許可權修飾符的),此處為預設的無參構造,如要產生有參的,只需傳入相應參數的類型Class對象Constructor cons = clazz.getDeclaredConstructor();//建立對象Car car = (Car) cons.newInstance();//根據傳入方法的參數的類型,擷取setBrand方法Method setBrand = clazz.getMethod("setBrand", String.class);//回調方法setBrand.invoke(car, "紅旗CA72");//同理Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class);setMaxSpeed.invoke(car, 200);
問題來了,如果我們擷取一個類的方法或屬性是非public的,我們訪問一個非public的方法或屬性時如果不加設定會拋出異常: java.lang.IllegalAccessException
我們只需要加一行代碼:setAccessible(true),通常不這樣做,有特殊要求時可以這麼解決;
// 擷取private屬性Field colorFld = clazz.getDeclaredField("color");// 設定可訪問colorFld.setAccessible(true);colorFld.set(pcar, "紅色");// 獲得private聲明的方法Method driveMtd = clazz.getDeclaredMethod("drive");// 設定可訪問driveMtd.setAccessible(true);driveMtd.invoke(pcar, (Object[]) null);
Java反射機制在諸多的架構中應用很廣泛,我們在開發中常用的也不多,如需要自己編寫一些架構,底層是逃離不了對Java反射的運用的,在JDK中reflect包中提供了這些類和介面,詳細的可以在API中找到。
上面的類載入器ClassLoader:說說它的工作機制 ClassLoader是一個重要的Java運行時環境組件,負責在運行時尋找和載入Class位元組碼檔案。類載入工作是有ClassLoader及其子類負責的。 JVM在運行時會產生三個ClassLoader,準確的說是兩個,因為根裝載器不是ClassLoader的子類,它使用C++編寫的,所以在Java中不能看到,看一個小程式:
public class ClassLoaderTest {public static void main(String[] args) {//擷取當前線程內容相關的類載入器ClassLoader loader = Thread.currentThread().getContextClassLoader();//ClassLoader loader = ClassLoaderTest.class.getClassLoader();System.out.println("Current loader:"+loader);//當前類載入器System.out.println("Parent loader:"+loader.getParent());//父載入器System.out.println("Grandparent loader:"+loader.getParent().getParent());}}輸出結果是:
Current loader:sun.misc.Launcher$AppClassLoader@6b97fdParent loader:sun.misc.Launcher$ExtClassLoader@1c78e57Grandparent loader:null
@ClassLoader:根裝載器,負責裝載JRE的核心類庫,如JRE目標下的charsets.jar和rt.jar等,在Java中訪問不到,無法獲得它的控制代碼,返回null。
@ExtClassLoader:是ClassLoader的子類,負責裝載JRE擴充目錄ext中的jar類包。 @AppClassLoader:是ExtClassLoader的子類,負責裝載Classpath路徑下的類包。
全盤負責委託機制: 全盤負責:當一個ClassLoader裝載一個類時,除非顯式地指定使用另一個ClassLoader,該類鎖依賴及引用的類也由這個ClassLoader載入。 委託機制:先委託父裝載器尋找目標,只有在找不到的情況下在從自己的類路徑中尋找並裝載目標類,這樣就避免了一個安全隱患, 譬如我一不小心自己寫了一個String(開始階段這樣寫過),委託機制或委託父裝載器去裝載String類,這樣就不會裝載我寫的這個String類了。
補充一下ClassLoader幾個常用的介面方法:
Class loadClass(String name) name參數指定類裝載器需要裝載類的名字,必須使用全限定類名,如com.baobaotao. beans.Car。
該方法有一個重載方法loadClass(Stringname ,boolean resolve),resolve參數告訴類裝載器是否需要解析該類。在初始化類之前,應考慮進行類解析的工作,
但並不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那麼就不需要進行解析。
Class findSystemClass(String name) 從本地檔案系統載入Class檔案,如果本地檔案系統不存在該Class檔案,將拋出ClassNotFoundException異常。該方法是JVM預設使用的裝載機制。
Class findLoadedClass(String name) 調用該方法來查看ClassLoader是否已裝入某個類。如果已裝入,那麼返回java.lang.Class對象,否則返回null。如果強行裝載已存在的類,將會拋出連結錯誤。
ClassLoadergetParent() 擷取類裝載器的父裝載器,除根裝載器外,所有的類裝載器都有且僅有一個父裝載器,ExtClassLoader的父裝載器是根裝載器,因為根裝載器非Java編寫,所以無法獲得,將返回null。
以下用一個圖描述類裝載器與對象的關係:
Object.getClass() Class ---執行個體對象獲得類位元組碼 Class.getClassLoader() ClassLoader ----類位元組碼對象獲得類裝載器 ClassLoader.loaderClass(string className) Class ----類裝載器轉載特定的類