1. Java動態代理InvocationHandler和Proxy學習筆記
Java動態代理機制中核心在於兩個類和介面:Proxy類和InvocationHandler介面。Proxy類用於創建代理對象,而InvocationHandler則是代理實例的調用處理程序介面。每一代理實例都關聯一個實現InvocationHandler介面的調用處理程序,當代理對象調用方法時,調用會被轉發至調用處理程序的invoke方法處理。實現InvocationHandler介面的關鍵在於定義代理類的調用處理程序,通過它,我們可以為代理類指定一組介面,代理類可調用介面中的所有方法。
Proxy類通過newProxyInstance方法創建代理類實例,該方法接收三個參數:classloader、interface數組、以及InvocationHandler對象。classloader定義代理類載入方式,interface數組表示代理類將實現的介面,而InvocationHandler則指明了代理對象調用方法時應關聯的處理程序。
在實際應用中,通過定義介面和實現類,以及創建代理類和調用處理程序,可實現動態代理的功能。例如,定義介面People,實現類Teacher,接著定義代理類的調用處理程序,實現InvocationHandler介面。在客戶端類中使用代理對象調用方法,輸出結果顯示代理機制的運行效果。
動態代理在Spring框架中扮演重要角色,尤其在面向切面編程(AOP)中,用於在代理類的前後添加不同切面。Spring中的AOP核心思想基於動態代理,通過代理類的前後加入切面實現功能的增強或修改。
除了新代理實例生成方法newProxyInstance,Proxy類還包含其他方法,如getInvocationHandler獲取指定代理實例的調用處理程序,getProxyClass返回給定類載入器和介面數組的代理類的Class對象,以及isProxyClass檢查代理類是否由getProxyClass或newProxyInstance方法動態生成。
2. Java 幾種動態代理實現及其性能比較
1. 動態代理是指在運行時,動態生成代理類。代理類的位元組碼將在運行時生成並載入當前的ClassLoader.
生成動態代理類的方法很多,如JDK自帶的動態代理、CGLIB、Javassist或者ASM庫。
JDK動態代理使用簡單,它內置在JDK中,因此不需要引入第三方Jar包,但相對功能比較弱。CGLIB和Javassist都是高級的位元組碼生成庫,總體性能比JDK自帶的動態代理好,而且功能十分強大。ASM是低級的位元組碼生成工具,使用ASM已經近乎在於使用Javabytecode編程,對開發人員要求較高,也是性能最好的一種動態代理生辰工具。但ASM的使用是在過於繁瑣,而且性能也沒有數量級的提升,與CGLIB等高級位元組碼生成工具相比,ASM程序的可維護性也較差。
JDK實現
1、步驟
1)通過實現InvocationHandler介面創建自己的調用處理器
2)通過為Proxy類指定ClassLoader對象和一組interface來創建動態代理類
3)通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器介面類型
4)通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數被傳入
2、創建代理
//InvocationHandlerImpl 實現了InvocationHandler介面,並能實現方法調用從代理類到委託類的分派轉發
//其內部通常包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用
InvocationHandler handler = new InvocaitonHandlerImpl(..);
//通過Proxy為包括Interface介面在內的一組介面動態創建代理類的對象
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{Interface.class,...});
//通過反射從生成的類對象獲得構造函數對象
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
//通過構造函數對象創建動態代理類實例
Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});
//Proxy類的靜態方法newProxyInstance對上面具體步驟的後三步做了封裝,簡化了動態代理對象的獲取過程。
//InvocationHandlerImpl實現了InvocaitonHandler介面,並能實現方法調用從代理類到委託類的分派轉發
InvocaitonHandler handler = new InvocationHandlerImpl(..);
//通過Proxy直接創建動態代理類實例
nterface proxy = (Interface)Proxy.newProxyInstance(classLoader,new Class[]{Interface.class},handler);
3、代碼
/**
* 介面
*
*/
public interface IDBQuery {
String request();
}
/**
* 真實的實現類,具體的目標對象
*
*/
public class DBQuery implements IDBQuery {
public DBQuery(){
try {
Thread.sleep(1000); //可能包含資料庫連接等耗時操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String request() {
return "request string";
}
}
/**
* JDK動態代理的實現類
*
*/
public class JdkDbQueryHandler implements InvocationHandler{
IDBQuery real = null; //主題介面
/**
* 生成Handler
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(real == null)
real = new DBQuery(); //如果是第一次調用,則生成真實對象
return real.request(); //使用真實主題完成實際的操作
}
/**
* 利用Handler生成動態代理對象
*/
public static IDBQuery createJdkProxy(){
//根據指定的類載入器和介面以及截獲器,返回代理類的一個實例對象
//ClassLoader loader :指定被代理對象的類載入器
//Class[] Interfaces :指定被代理對象所以事項的介面
//InvocationHandler h :指定需要調用的InvocationHandler對象
IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdkDbQueryHandler());
return jdkProxy;
}
}