設計模式之禪學習筆記09--代理模式(動態代理)
1.動態代理
動態代理還是屬於設計模式--代理模式的一種,代理類在程式執行時建立的代理方式被成為動態代理。動態代理是在實現階段不用關心代理誰,而在執行階段才指定代理哪一個物件。相對來說,自己寫代理類的方式就是靜態代理。現在有一個非常流行的名稱叫做面向橫切面程式設計,也就是AOP(Aspect Oriented Programming),其核心就是採用了動態代理機制。
類圖:
很簡單,兩條獨立發展的線路。動態代理實現代理的職責,業務邏輯Subject實現相關的邏輯功能,兩者之間沒有必然的相互耦合的關係。通知Advice從另一個切面切入,最終在高層模組也就是Client進行耦合,完成邏輯的封裝任務。
2.舉個例子
兩條線,首先建立業務邏輯這條線,建立一個抽象主題類
package com.wx.dynamicproxy.base; /* 抽象主題 */ public interface Subject { public void doSomething(String string); }
建立具體的主題類,實現抽象主題介面
package com.wx.dynamicproxy.imp; import com.wx.dynamicproxy.base.Subject; /* 真實主題 */ public class RealSubject implements Subject { /* 業務操作 */ @Override public void doSomething(String string) { System.out.println("真實主題的業務操作"+string); } }
第二條線,動態代理實現代理的職責。
建立動態代理類:DynamicProxy,這個類中使用JDK提供的Proxy類來動態建立物件,這裡也是動態代理和靜態代理的區別,靜態代理這個Proxy需要自己寫。需要傳入一下三個引數
loader:指定當前目標物件使用類載入器,獲取載入器的方法是固定的
interfaces:目標物件實現的介面的型別(所以目標物件如果不實現介面就無法使用動態代理),使用泛型方式確認型別
InvocationHandler h:事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的
package com.wx.dynamicproxy.imp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/*
動態代理類
方法作為引數傳入
*/
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h){
//執行目標,並返回結果,
return (T)Proxy.newProxyInstance(loader,interfaces, h);
}
}
代理類的三個引數中,只有InvocationHandler 需要自己寫,所以建立動態代理的動態代理的MyInvocationHandler類。實現介面InvocationHandler,這個類中持有一個被代理物件的例項target,並且所有通過動態代理實現的方法全部通過invoke方法呼叫。
package com.wx.dynamicproxy.imp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
動態代理的MyInvocationHandler類,
*/
public class MyInvocationHandler implements InvocationHandler {
//這個類中持有一個被代理物件的例項target
private Object target=null;
//通過建構函式傳遞一個物件
public MyInvocationHandler(Object o)
{
this.target=o;
}
//代理方法,非常簡單,所有通過動態代理實現的方法全部通過invoke方法呼叫。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//執行被代理的方法
return method.invoke(target,args);
}
}
測試:代理的過程是沒有問題的了
package com.wx.dynamicproxy.test;
import com.wx.dynamicproxy.base.Subject;
import com.wx.dynamicproxy.imp.DynamicProxy;
import com.wx.dynamicproxy.imp.MyInvocationHandler;
import com.wx.dynamicproxy.imp.RealSubject;
import com.wx.dynamicproxy.imp.SubjectDynamicProxy;
public class Client {
public static void main(String[] agrs)
{
//定義一個主題
Subject subject=new RealSubject();
//定義一個Handler
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(subject);
//定義主題的代理
Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),myInvocationHandler);
/*Subject proxyInstance = SubjectDynamicProxy.newProxyInstance(subject);*/
proxy.doSomething("hello");
}
}
實現了一個簡單的橫切面程式設計,我們來看通知Advice,也就是我們要切入的類。
建立一個通知介面IAdvice
package com.wx.dynamicproxy.base;
public interface IAdvice {
//通知只有一個方法,執行即可
public void exec();
}
實現這個介面:
package com.wx.dynamicproxy.imp;
import com.wx.dynamicproxy.base.IAdvice;
public class BeforeAdvice implements IAdvice {
@Override
public void exec() {
System.out.println("我是前置通知,我被執行了");
}
}
在動態代理類中把它切進去
package com.wx.dynamicproxy.imp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/*
動態代理類
loader:指定當前目標物件使用類載入器,獲取載入器的方法是固定的
interfaces:目標物件實現的介面的型別,使用泛型方式確認型別
InvocationHandler h:事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的方法作為引數傳入
*/
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h){
//尋找JoinPoint連線點,AOP框架使用元資料
if(true){
//執行一個前置通知
(new BeforeAdvice()).exec();
}
//執行目標,並返回結果,
return (T)Proxy.newProxyInstance(loader,interfaces, h);
}
}
測試:
代理模式應用得非常廣泛,大到一個系統框架、企業平臺,小到程式碼片段、事務處理,稍不留意就用到代理模式。可能該模式是大家接觸最多的模式,而且有了AOP大家寫代理就更加簡單了,有類似Spring AOP和AspectJ這樣非常優秀的工具,拿來主義可!不過,大家可以看看原始碼,特別是除錯時,只要看到類似$Proxy0這樣的結構,你就應該知道這是一個動態代理了。
友情提醒,在學習AOP框架時,弄清楚幾個名詞就成:切面(Aspect)、切入點(JoinPoint)、通知(Advice)、織入(Weave)就足夠了,理解了這幾個名詞,應用時你就可以遊刃有餘了!