代理模式(Proxy Pattern)給某一個物件提供一個代理,並由代理物件控制原物件的參照。代理物件在使用者端和目標物件之間起到中介作用 。
代理模式是常用的結構型設計模式之一,當直接存取某些物件存在問題時可以通過一個代理物件來間接存取。
在Java的動態代理機制中,有一個介面InvocationHandler
(Interface)和另一個類 Proxy
(Class),二者可謂之中流砥柱。
在使用動態代理的時候,每一個代理類需要實現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動態代理分為以下幾步:
參考:https://blog.csdn.net/jiankunking/article/details/52143504
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來實現動態代理。
基於動態代理