動態代理學習小結
動態代理
1.代理模式
1.1什麼是代理模式
-
代理模式是指,為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,
一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶類和目標物件之
間起到中介的作用。 -
例如: 有 A, B(代理類), C (目標類)三個類, A 原來可以呼叫 C 類的方法, 現在因為某種原因 C 類不允許A 類呼叫其方法,但 B 類可以呼叫 C 類的方法。 A 類通過 B 類呼叫 C 類的方法。這裡 B 是 C 的代理。 A 通過代理 B 訪問 C。
1.2代理模式作用
- 功能增強: 在原有的功能上,增加了額外的功能。 新增加的功能,叫做功能增強。
- 控制訪問: 代理類不讓你直接訪問目標,例如中介不讓租戶直接訪問房東。
1.3代理模式分類
-
靜態代理
-
動態代理
2.靜態代理
2.1什麼是靜態代理
- 靜態代理是指,代理類在程式執行前就已經定義好.java 原始檔,其與目標類的關係在
程式執行前就已經確立。 在程式執行前代理類已經編譯為.class 檔案。 - (1)代理類是自己手工實現的,自己手動建立一個java類,表示代理類。
(2)同時你所要代理的目標類是確定的。
特點: 1)實現簡單 2)容易理解。
2.2靜態代理缺點
- 當一個專案中,目標類和代理類很多時候,有以下的缺點:
(1)程式碼複雜,難於管理
代理類和目標類實現了相同的介面, 每個代理都需要實現目標類的方法, 這樣就出現了大量的代
此方法。增加了程式碼維護的複雜度 ,耦合度也高。
(2)代理類依賴目標類,代理類過多
代理類只服務於一種型別的目標類,如果要服務多個型別。勢必要為每一種目標類都進行代理,
靜態代理在程式規模稍大時就無法勝任了,代理類數量過多。
3.動態代理
3.1什麼是動態代理
- 動態代理是指代理類物件在程式執行時由JVM 根據反射機制動態生成的。 動態代理不
需要定義代理類的.java 原始檔。
動態代理其實就是 jdk 執行期間, 動態建立 class 位元組碼並載入到 JVM。
3.2動態代理的實現方式
-
jdk動態代理(理解): 使用java反射包中的類和介面實現動態代理的功能。是基於介面的代理類
反射包 java.lang.reflect , 裡面有三個類 : InvocationHandler , Method, Proxy。 -
cglib動態代理(瞭解): cglib是第三方的工具庫, 建立代理物件。
cglib的原理是繼承,cglib通過繼承目標類,建立它的子類,在子類中重寫父類中同名的方法, 實現功能的修改。因為cglib是繼承,重寫方法,所以要求目標類不能是final的, 方法也不能是final的。cglib的要求目標類比較寬鬆, 只要能繼承就可以了。cglib在很多的框架中使用, 比如 mybatis ,spring框架中都有使用。
3.3jdk動態代理需要用到的方法和類
-
(1) InvocationHandler 介面
通 過 代 理 對 象 執 行 目 標 接 口 中 的 方 法 , 會 把 方 法 的 調 用 分 派 給 調 用 處 理 器
(InvocationHandler)的實現類,執行實現類中的 invoke()方法,我們需要把功能代理寫在 invoke()方法中public interface InvocationHandler {//代理類必須實現這個介面,並重寫invoke()方法 /** * 該方法在目標類的方法被執行的時候,會被呼叫 * 呼叫目標類方法時,會先執行該invoke方法 * @param proxy 真正的代理物件,即Proxy.newProxyInstance()方法的返回結果 * @param method 目標類中的方法,jdk自動幫我們提供物件 * @param args 目標類中方法的引數(可變引數),也是jdk自動幫我們的提供的 * @return null * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
-
(2) Method 類
表示目標類中的方法
InvocationHandler實現類的invoke()方法的第二個引數為 Method 類物件,該類有一個方法也叫 invoke(),可以呼叫目標方法。這兩個 invoke()方法,雖然同名,但無關
public Object invoke(Object obj, Object... args){} //obj:表示目標物件 //args:表示目標方法引數,就是InvocationHandler實現類的invoke方法的第三個引數(可變引數)
該方法的作用是:呼叫執行 obj 物件所屬類的方法,這個方法由其呼叫者 Method 物件確定。
在程式碼中,一般的寫法為
method.invoke(target, args);
其中, method 為上一層 invoke 方法的第二個引數。這樣,即可呼叫目標類的目標方法。 -
( 3) Proxy 類
通 過 JDK 的 java.lang.reflect.Proxy 類 實 現 動 態 代 理 , 使 用 其 靜 態 方 法newProxyInstance(),依據目標物件、業務介面及呼叫處理器三者,自動生成一個動態代理物件。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {} //loader:目標類的類載入器,通過目標物件的反射可獲取 //interfaces:目標類實現的介面陣列,通過目標物件的反射可獲取 //handler: 呼叫處理器。
3.4 jdk 動態代理實現
jdk 動態代理是代理模式的一種實現方式,其只能代理介面。實現步驟:
1、新建一個介面,作為目標介面
2、為介面建立一個實現類,是目標類
3、建立類實現 java.lang.reflect.InvocationHandler 介面,呼叫目標方法並增加其他功能程式碼
4、 建立動態代理物件,使用 Proxy.newProxyInstance()方法,並把返回值強制轉為介面型別。
3.5jdk動態代理的例子
- 目標介面
public interface TargetClass {
void sayHello();
}
-
目標介面的實現類
public class TargetClassImpl implements TargetClass{ @Override public void sayHello() { System.out.println("Hi,dynamic proxy"); } }
-
建立類實現 java.lang.reflect.InvocationHandler 介面
/** * ProxyClass是動態代理的一部分,還不是真正的代理類,該類是協助真正的代理類去工作 */ public class ProxyClass implements InvocationHandler { private Object target;//這裡要傳入一個目標類 public ProxyClass(Object target) { this.target = target; } /** * 該方法在目標類的方法被執行的時候,會被呼叫 * 呼叫目標類方法時,會先執行該invoke方法 * @param proxy 真正的代理物件,即Proxy.newProxyInstance()方法的返回結果 * @param method 目標類中的方法,jdk自動幫我們提供物件 * @param args 目標類中方法的引數(可變引數),也是jdk自動幫我們的提供的 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目標方法執行前。。。。。。"); //此程式碼是真正的目標方法sayHello() Object result = method.invoke(target,args); //這些增加的方法表示增強功能作用 System.out.println("目標方法執行後。。。。。。"); return result; } }
-
建立動態代理物件來呼叫目標類
public class Client { public static void main(String[] args) { //使用代理模式 //Proxy.newProxyInstance()這個方法的返回結果才是我們的真正的代理物件 TargetClass targetClass = (TargetClass)Proxy.newProxyInstance(TargetClass.class.getClassLoader(), new Class<?>[] {TargetClass.class}, new ProxyClass(new TargetClassImpl())); targetClass.sayHello(); } }