標籤:voc 部分 odi 編寫 tin framework after iar ati
一、何為動態代理?
建議看動態代理前,先看看反射 點擊這裡
先看一個小案例,假設有咖啡類A,B,C。有添加物類a,b,c,。現在對咖啡類進行增強。( 比如像向啡中加糖,牛奶等這個意思)。
對一個類進行增強可以有三種方式:
1.繼承,可以直接繼承父類的屬性和方法,在新增自己的屬性和方法。那麼我們對每種咖啡進行增強,一共需要寫3*3個繼承類來完成。增強對象和內容都不可變。
2.裝飾者模式,在需要增強類中組合一個需要增強屬性。那麼我們需要寫三個組合咖啡類,每個類都添加一個添加物介面屬性,向添加物介面傳不同的實現就可以對該咖啡類進行不同的增強。增強內容可變,但增強對象是不可變的。
3.動態代理,通過一個代理工廠,向其傳入需要增強咖啡類和添加物類,其自動產生一個增強後的代理類。我們只需要實現代理工廠這個類即可。
二、java動態代理的實現
java中有一個類Proxy,其有一個靜態方法Object proxy = Proxy.newProxyInstance(ClassLoader classLoader , Class[] interfaces , InvocationHandler); 通過此方法就可以得到一個代理對象。下面說一下這三個參數。
ClassLoader 類載入器,負責載入代理的類載入器。關於類載入器詳細解答 點擊這裡
Class[] 目標類所實現的介面們,用Class對象數組表示。
Invocationhandler 處理器,當調用代理類proxy的方法時,實際上是調用處理器的 invoke()方法。下面在對invoke方法進行詳解。
Object invoke(Object proxy , Method method , Object[] args ) ;
proxy:代理對象。 method: 執行的代理對象的方法。 args: 調用代理對象的方法時傳入的參數。
下面貼代碼實現
目標類介面
package cn.edu;public interface Waiter { public void server();}
前置通知介面
package cn.edu;public interface AdviceBefore { public void before();}
後置通知介面
package cn.edu;public interface AdviceAfter { public void after();}
代理工廠
package cn.edu;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 代理工廠,用於生產代理對象 * * @author WangHang * */public class ProxyFactory { private Object target; // 目標對象 private AdviceBefore adviceBefore; // 前置通知 private AdviceAfter adviceAfter; // 後置通知 // 組建代理程式對象核心方法 public Object getProxyInstance() { ClassLoader loader = ProxyFactory.class.getClassLoader(); // 類載入器 Class[] interfaces = target.getClass().getInterfaces(); // 類所實現的介面 InvocationHandler h = new InvocationHandler() { // 處理器 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (adviceBefore != null) { adviceBefore.before(); // 調用前置通知 } Object result = method.invoke(target, args); // 調用目標方法 if (adviceAfter != null) { // 調用後置通知 adviceAfter.after(); } return result; } }; Object proxy = Proxy.newProxyInstance(loader, interfaces, h); // 通過給定的三個參數產生動態代理對象 return proxy; // 返回代理對象 } public AdviceBefore getAdviceBefore() { return adviceBefore; } public void setAdviceBefore(AdviceBefore adviceBefore) { this.adviceBefore = adviceBefore; } public AdviceAfter getAdviceAfter() { return adviceAfter; } public void setAdviceAfter(AdviceAfter adviceAfter) { this.adviceAfter = adviceAfter; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; }}
測試,介面的實現用的匿名內部類
package cn.edu;import org.junit.Test;public class ProxyTest { @Test public void fun() { Waiter waiter = new Waiter() { //產生目標對象 @Override public void server() { System.out.println("服務中。。。"); } }; AdviceBefore before = new AdviceBefore() { //產生前置通知 @Override public void before() { System.out.println("歡迎光臨!"); } }; AdviceAfter after = new AdviceAfter() { @Override public void after() { System.out.println("謝謝光臨!"); //產生後置通知 } }; ProxyFactory proxyFactory = new ProxyFactory(); //產生工廠 proxyFactory.setTarget(waiter); //設定目標對象 proxyFactory.setAdviceBefore(before); //設定前置通知 proxyFactory.setAdviceAfter(after); //設定後置通知 Waiter proxy = (Waiter)proxyFactory.getProxyInstance(); //獲得代理對象 proxy.server(); //調用目標對象的方法 } }
運行結果:
三、cglib動態代理的實現
java動態代理的實現要求目標類實現介面,如果沒有介面就無法完成動態代理。cglib(Code Genaration Liarbry)是一個強大的Code產生類庫 ,它可以在程式運行期間擴充Java類。它不要求目標類實現介面,它採用的是繼承的方式來擴充目標類。
下面用cglib實現代理,其餘類不用變,只需更改代理工廠,並且給Waiter添加一個實作類別WaiterImpl,cglib代理是需要類的,用匿名類是會報錯的。更改的部分如下。
Waiter實作類別
package cn.edu.cglibProxy;public class WaiterImpl implements Waiter { @Override public void server() { // TODO Auto-generated method stub System.out.println("服務中。。。"); }}
代理工廠類
package cn.edu.cglibProxy;import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;/** * 代理工廠,用於生產代理對象 * * @author WangHang * */public class ProxyFactory { private Object target; // 目標對象 private AdviceBefore adviceBefore; // 前置通知 private AdviceAfter adviceAfter; // 後置通知 // 組建代理程式對象核心方法 public Object getProxyInstance() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { adviceBefore.before(); Object result = method.invoke(target, args); adviceAfter.after(); return result; } }); Object proxy = enhancer.create(); return proxy; // 返回代理對象 } public AdviceBefore getAdviceBefore() { return adviceBefore; } public void setAdviceBefore(AdviceBefore adviceBefore) { this.adviceBefore = adviceBefore; } public AdviceAfter getAdviceAfter() { return adviceAfter; } public void setAdviceAfter(AdviceAfter adviceAfter) { this.adviceAfter = adviceAfter; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; }}
測試類別
package cn.edu.cglibProxy;import org.junit.Test;public class ProxyTest { @Test public void fun() { Waiter waiter = new WaiterImpl(); AdviceBefore before = new AdviceBefore() { //產生前置通知 @Override public void before() { System.out.println("歡迎光臨!"); } }; AdviceAfter after = new AdviceAfter() { @Override public void after() { System.out.println("謝謝光臨!"); //產生後置通知 } }; ProxyFactory proxyFactory = new ProxyFactory(); //產生工廠 proxyFactory.setTarget(waiter); //設定目標對象 proxyFactory.setAdviceBefore(before); //設定前置通知 proxyFactory.setAdviceAfter(after); //設定後置通知 Waiter proxy = (Waiter)proxyFactory.getProxyInstance(); //獲得代理對象 proxy.server(); //調用目標對象的方法 } }
測試結果
四、總結
通過上面的兩個例子,可以體會到動態代理的優勢。靜態代理是由程式員手動編寫好原始碼,在程式運行時已經存在了class檔案,而動態代理則是在運行時根據需要動態產生的,不僅減少了工作量,使用時也更加靈活。
Java動態代理和cglib動態代理