Java動態代理詳解
說到動態代理,顧名思義就是動態的代理(真是廢話)。
關於代理:想必大家都並不陌生,GOF的23種設計模式之一(結構型模式)。這裏暫不多做介紹,有興趣的可以關注我關於設計模式的文章。
什麼是動態代理:
說起動態,其實不如先說什麼是靜態。所謂靜態代理,個人理解爲自己手寫的代理類,或者用工具生成的代理類,或者別人幫你寫的代理類(沒說一樣...)。總之,就是程序運行前就已經存在的編譯好的代理類。
相反,如果代理類程序運行前並不存在,需要在程序運行時動態生成(無需手工編寫代理類源碼),那就是今天要說的動態代理了。
如何生成的:根據Java的反射機制動態生成。
不多說了,上程序。
目標接口TargetInterface:
- public interface TargetInterface {
- public int targetMethodA(int number);
- public int targetMethodB(int number);
- }
很簡單,一個普通的接口,裏面有若干方法(此處寫2個示範一下)
實現該接口的委託類ConcreteClass:
- public class ConcreteClass implements TargetInterface{
- public int targetMethodA(int number) {
- System.out.println("開始調用目標類的方法targetMethodA...");
- System.out.println("操作-打印數字:"+number);
- System.out.println("結束調用目標類的方法targetMethodA...");
- return number;
- }
- public int targetMethodB(int number){
- System.out.println("開始調用目標類的方法targetMethodB...");
- System.out.println("操作-打印數字:"+number);
- System.out.println("結束調用目標類的方法targetMethodB...");
- return number;
- }
- }
很簡單,一個普通的類,實現了目標接口。
代理處理器類ProxyHandler:
- public class ProxyHandler implements InvocationHandler{
- private Object concreteClass;
- public ProxyHandler(Object concreteClass){
- this.concreteClass=concreteClass;
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("proxy:"+proxy.getClass().getName());
- System.out.println("method:"+method.getName());
- System.out.println("args:"+args[0].getClass().getName());
- System.out.println("Before invoke method...");
- Object object=method.invoke(concreteClass, args);//普通的Java反射代碼,通過反射執行某個類的某方法
- //System.out.println(((ConcreteClass)concreteClass).targetMethod(10)+(Integer)args[0]);
- System.out.println("After invoke method...");
- return object;
- }
- }
該類實現了Java反射包中的InvocationHandler接口。代理實例調用方法時,將對方法調用指派到它的代理處理器程序的invoke方法中。invoke方法內部實現預處理,對委託類方法調用,事後處理等邏輯。
最後是入口程序:
- public class DynamicProxyExample {
- public static void main(String[] args){
- ConcreteClass c=new ConcreteClass();//元對象(被代理對象)
- InvocationHandler ih=new ProxyHandler(c);//代理實例的調用處理程序。
- //創建一個實現業務接口的代理類,用於訪問業務類(見代理模式)。
- //返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序,如ProxyHandler。
- TargetInterface targetInterface=
- (TargetInterface)Proxy.newProxyInstance(c.getClass().getClassLoader(),c.getClass().getInterfaces(),ih);
- //調用代理類方法,Java執行InvocationHandler接口的方法.
- int i=targetInterface.targetMethodA(5);
- System.out.println(i);
- System.out.println();
- int j=targetInterface.targetMethodB(15);
- System.out.println(j);
- }
- }
首先創建委託類對象,將其以構造函數傳入代理處理器,代理處理器ProxyHandler中會以Java反射方式調用該委託類對應的方法。然後使用Java反射機制中的Proxy.newProxyInstance方式創建一個代理類實例,創建該實例需要指定該實例的類加載器,需要實現的接口(即目標接口),以及處理代理實例接口調用的處理器。
最後,調用代理類目標接口方法時,會自動將其轉發到代理處理器中的invoke方法內,invoke方法內部實現預處理,對委託類方法調用,事後處理等邏輯。
使用Java動態代理機制的好處:
1、減少編程的工作量:假如需要實現多種代理處理邏輯,只要寫多個代理處理器就可以了,無需每種方式都寫一個代理類。
2、系統擴展性和維護性增強,程序修改起來也方便多了(一般只要改代理處理器類就行了)。
使用Java動態代理機制的限制:
目前根據GOF的代理模式,代理類和委託類需要都實現同一個接口。也就是說只有實現了某個接口的類可以使用Java動態代理機制。但是,事實上使用中並不是遇到的所有類都會給你實現一個接口。因此,對於沒有實現接口的類,目前無法使用該機制。有人說這不是廢話嗎,本來Proxy模式定義的就是委託類要實現接口的啊!但是沒有實現接口的類,該如何實現動態代理呢?
當然不是沒有辦法,這也是我後面抽時間要繼續整理和總結原先使用過的一件神器,相關Blog會不定期發上來。那就是大名鼎鼎的CGLib...
PS:CGLib中的動態代理已經新鮮出爐,歡迎訪問:http://shensy.iteye.com/blog/1873155