定義
代理(Proxy)是一種設計模式,提供了對目標對象另外的訪問方式;即通過代理對象訪問目標對象.這樣做的好處是:可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。有點類似於裝飾者模式。但是與裝飾者模式的區別是代理模式最終不一定調用目標對象的目標方法。但裝飾者一定會。
圖示:
理解
通俗一點說,就是代理對象持有目標對象的引用,並對外提供所有目標對象的業務方法,在該方法中添加一些邏輯處理並決定是否調用目標對象方法。代理模式的關鍵點是:代理對象與目標對象.代理對象是對目標對象的擴展,並會根據情況調用目標對象。代理模式又分爲靜態代理和動態代理。下面我們分別做介紹。
靜態代理
角色:
靜態代理的內容比較簡單,這裏我們先借鑑網上的一個很貼切的例子來說明,然後通過例子中總結一下。
實例
public interface ICoder {
public void implDemands(String demandName);
}
2、定義真實角色類:開發人員
public class JavaCoder implements ICoder{
private String name;
public JavaCoder(String name){
this.name = name;
}
@Override
public void implDemands(String demandName) {
System.out.println(name + " implemented demand:" + demandName + " in JAVA!");
}
public class CoderProxy implements ICoder{
private ICoder coder;
public CoderProxy(ICoder coder){
this.coder = coder;
}
@Override
public void implDemands(String demandName) {
coder.implDemands(demandName);
}
}
4、爲代理的方法添加額外的業務邏輯public class CoderProxy implements ICoder{
private ICoder coder;
public CoderProxy(ICoder coder){
this.coder = coder;
}
@Override
public void implDemands(String demandName) {
if(demandName.startsWith("Add")){
System.out.println("No longer receive 'Add' demand");
return;
}
coder.implDemands(demandName);
}
}
4、測試
public class Customer {
public static void main(String args[]){
//定義一個java碼農
ICoder coder = new JavaCoder("Zhang");
//定義一個產品經理
ICoder proxy = new CoderProxy(coder);
//讓產品經理實現一個需求
proxy.implDemands();
}
}
- Subject:抽象主題角色。可以是接口,也可以是抽象類。
- RealSubject:真實主題角色。業務邏輯的具體執行者。
- ProxySubject:代理主題角色。內部含有RealSubject的引用,負責對真實角色的調用,並在真實主題角色處理前後做預處理和善後工作。
- 職責清晰 真實角色只需關注業務邏輯的實現,非業務邏輯部分,後期通過代理類完成即可。
- 高擴展性 不管真實角色如何變化,由於接口是固定的,代理類無需做任何改動。
動態代理
public class CoderDynamicProxy implements InvocationHandler{
//被代理的實例
private ICoder coder;
public CoderDynamicProxy(ICoder _coder){
this.coder = _coder;
}
//調用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(System.currentTimeMillis());
Object result = method.invoke(coder, args);
System.out.println(System.currentTimeMillis());
return result;
}
}
public class DynamicClient {
public static void main(String args[]){
//要代理的真實對象
ICoder coder = new JavaCoder("Zhang");
//創建中介類實例
InvocationHandler handler = new CoderDynamicProxy(coder);
//獲取類加載器
ClassLoader cl = coder.getClass().getClassLoader();
//動態產生一個代理對象
ICoder proxy = (ICoder) Proxy.newProxyInstance(cl, coder.getClass().getInterfaces(), handler);
//通過代理類,執行doSomething方法;這裏就走到了CoderDynamicProxy.invoke()中
proxy.implDemands("Modify user management");
}
}
執行結果如下:
1501728574978
Zhang implemented demand:Modify user management in JAVA!
1501728574979
- 第一個參數 handler.getClass().getClassLoader() ,我們這裏使用handler這個類的ClassLoader對象來加載我們的代理對象
- 第二個參數coder.getClass().getInterfaces(),我們這裏爲代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了
- 第三個參數handler, 我們這裏將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
總結一下,一個典型的動態代理可分爲以下五個步驟:
1、創建抽象角色
2、創建真實角色
3、通過實現InvocationHandler接口創建中介類
4、通過Proxy.newProxyInstance()生成一個動態代理對象
5、通過代理對象調用業務方法
動態代理的使用基本就是這樣,接下來我們深入JDK看看java是如何做到動態生成一個代理類,代碼又是如何執行到中介類InvocationHandler.invoke()裏面的。
進入Proxy.newProxyInstance()源碼中查看主要邏輯如下:
//生成代理類class,並加載到jvm中,獲取Class對象
Class<?> cl = getProxyClass0(loader, interfaces);
//獲取代理類參數爲InvocationHandler的構造函數
final Constructor<?> cons = cl.getConstructor(constructorParams);
//生成代理類的實例對象,並返回
return newInstance(cons, ih);
我們一步一步分析上述幾行代碼
第一步生成代理類,該例中生成的便是實現ICoder接口的類,並將該類加載到jvm中,進入getProxyClass0()中核心邏輯如下:
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
通過Class.forName()生成代理類並加載到jvm中,返回Class對象。Calss.forName()中調用的是native方法
第二步獲取構造函數
我們可以看到constructorParams是Proxy維護的靜態變量,其中包括InvocationHandler,源碼如下:
/** parameter types of a proxy class constructor */
private final static Class[] constructorParams =
{ InvocationHandler.class };
第三步通過構造函數創建實例,參數爲構造器和構造時需要使用的中介類InvocationHandler
private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
try {
return cons.newInstance(new Object[] {h} );
} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString());
}
}
}
這樣,就解決了我們提出的第一個問題:如何生成代理對象。其實這部分涉及到類加載機制相關的知識,不熟悉的同學可以先學習這部分內容。那麼,爲什麼我們調用代理對象的業務方法,就會執行到中介類InvocationHandler.invoke()方法中呢?這就有從生成的代理類代碼中尋找答案了。我們可以手動模擬生成代理類,如下:
public class CodeUtil {
public static void main(String[] args) throws IOException {
byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", JavaCoder.class.getInterfaces());
File file = new File("D:/aaa/TestProxyGen.class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
}
注意這裏的類名TestProxyGen是我們模擬的,真正的類名還是$Proxy0
我們查看生成代理類的代碼,其中,業務方法implDemands如下:
這裏調用了h也就是InvocationHandler的invoke方法,這就能解釋爲何我們調用代理對象的業務方法,最終走到了中介類的invoke()方法中了