設計模式之代理模式(Proxy Pattern)
1、代理模式
1.1、介紹
概念
代理模式(Proxy Pattern)給某一個對象提供一個代理,並由代理對象控制原對象的引用。代理對象在客戶端和目標對象之間起到中介作用 。
代理模式是常用的結構型設計模式之一,當直接訪問某些對象存在問題時可以通過一個代理對象來間接訪問。
用途:
- 當提供服務方不想讓用戶訪問真正角色時,採用代理模式
- 當需要橫切一些業務時,為了不破壞原有的類,也可採用代理模式
作用:
- 功能增強: 在你原有的功能上,增加了額外的功能。 新增加的功能,叫做功能增強。
- 控制訪問: 代理類不讓你訪問目標,例如商家不讓用戶訪問廠家。
實現代理的方式
- 靜態代理
- 動態代理(兩種JDK和CGLIB)
1.2 JDK動態代理
在Java的動態代理機制中,有一個接口InvocationHandler
(Interface)和另一個類 Proxy
(Class),二者可謂之中流砥柱。
InvocationHandler
在使用動態代理的時候,每一個代理類需要實現InvocationHandler
接口,並且重寫其接口中唯一的方法invoke()。
動態代理工具類
public class DynamicProxy implements InvocationHandler { // 實現調用處理接口
//被代理的接口對象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//創建動態代理對象用Proxy類中的newProxyInstance()方法
// 參數信息:
// 參數一:通過反射得到類加載器
// 參數二:需要實現的接口
// 參數三:處理者 這個參數需要一個InvocationHandler(接口)的對象,
// 我們這個自定義代理類實現了InvocationHandler接口,所以用this調用自己
// 返回指定接口的代理類的實例,該接口將方法調用分派給指定的調用處理程序。
//Proxy.newProxyInstance因為與IllegalArgumentException相同的原因而Proxy.getProxyClass
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// 處理代理實例,並返回結果
// method這個參數其實就是我們要增強的方法,也就是需要代理類去調用的方法,
// 通過這個參數調用invoke()方法 invoke翻譯:調用
// method.invoke()通過反射去調用我們target接口裡的方法 動態之所以就在這
// 該方法會在動態代理過程中通過反射被執行,具體執行過程在下面解釋
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
return method.invoke(target, args);
}
private void log(String msg){
System.out.println("我是代理類添加的消息,使用了"+msg+"方法!!!");
}
}
動態代理的步驟
-
通過實現 InvocationHandler 接口創建自己的調用處理器(動態代理類);
-
通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創建動態代理類;
-
通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器接口類型;
-
//獲取代理對象的構造方法(也就是$Proxy0(InvocationHandler h)) final Constructor<?> cons = cl.getConstructor(constructorParams);
-
-
通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數被傳入。
-
//生成代理類的實例並把InvocationHandlerImpl的實例傳給它的構造方法,InvocationHandler h return cons.newInstance(new Object[]{h})
-
JDK動態代理步驟
JDK動態代理分為以下幾步:
- 拿到被代理對象的引用,並且通過反射獲取到它的所有的接口。
- 通過JDK Proxy類重新生成一個新的類,同時新的類要實現被代理類所實現的所有的接口。
- 動態生成 Java 代碼,把新加的業務邏輯方法由一定的邏輯代碼去調用。
- 編譯新生成的 Java 代碼.class。
- 將新生成的Class文件重新加載到 JVM 中運行。
參考://blog.csdn.net/jiankunking/article/details/52143504
1.3 CGLIB動態代理
JDK動態代理是通過重寫被代理對象實現的接口中的方法來實現,而CGLIB是通過繼承被代理對象來實現,和JDK動態代理需要實現指定接口一樣,CGLIB也要求代理對象必須要實現MethodInterceptor
接口,並重寫其唯一的方法intercept
。
CGLib採用了非常底層的位元組碼技術,其原理是通過位元組碼技術為一個類創建子類,並在子類中採用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。(利用ASM開源包,對代理對象類的class文件加載進來,通過修改其位元組碼生成子類來處理)
注意:因為CGLIB是通過繼承目標類來重寫其方法來實現的,故而如果是final和private方法則無法被重寫,也就是無法被代理。
1、JDK動態代理具體實現原理:
通過實現InvocationHandler接口創建自己的調用處理器;
通過為Proxy類指定ClassLoader對象和一組interface來創建動態代理;
通過反射機制獲取動態代理類的構造函數,其唯一參數類型就是調用處理器接口類型;
通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數參入;
JDK動態代理是面向接口的代理模式,如果被代理目標沒有接口那麼Spring也無能為力,Spring通過Java的反射機制生產被代理接口的新的匿名實現類,重寫了其中AOP的增強方法。
2、CGLib動態代理:
利用ASM開源包,對代理對象類的class文件加載進來,通過修改其位元組碼生成子類來處理。
3、兩者對比:
JDK動態代理是面向接口的。
CGLib動態代理是通過位元組碼底層繼承要代理類來實現,因此如果被代理類被final關鍵字所修飾,會失敗。
4、使用注意:
如果要被代理的對象是個實現類,那麼Spring會使用JDK動態代理來完成操作(Spirng默認採用JDK動態代理實現機制);
如果要被代理的對象不是個實現類那麼,Spring會強制使用CGLib來實現動態代理。
2、AOP的實現
基於動態代理