模式的祕密-代理模式(2)-JDK動態代理

代理模式-動態代理

(1)java

(2)編程

 

代碼實踐動態代理:dom

第一步:被代理類的接口:ide

package com.JdkProxy; public interface Moveable { void move(); }

第二步:被代理類:post

package com.JdkProxy; import java.util.Random; public class Car implements Moveable { @Override public void move() { //實現開車
        try { Thread.sleep(new Random().nextInt(1000)); System.out.println("汽車行駛中...."); } catch (InterruptedException e) { e.printStackTrace(); } } }

第三步:代理類:實現接口:InvocationHandler,同時把被代理類對象接口傳入構造方法,學習

重寫的接口的invoke方法。測試

package com.JdkProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TimeHandler implements InvocationHandler { private Object target; public TimeHandler(Object target) { this.target=target; } /* * 參數: * proxy:被代理對象 * method:被代理對象方法 * arg:方法的參數 * 返回值: * Object 方法的返回值 * */ @Override public Object invoke(Object proxy, Method method, Object[] arg) throws Throwable { long starttime=System.currentTimeMillis(); System.out.println("汽車開始形式...."); method.invoke(target); long endtime=System.currentTimeMillis(); System.out.println("汽車結束行駛...汽車形式時間:"+(endtime-starttime)+"毫秒"); return null; } }

測試類中實現代理:this

使用Proxy類的newProxyInstance方法產生一個被代理類的實例,該實例能夠看成被代理類使用接口(對應cls.getInterfaces())中聲明過的方法。spa

package com.JdkProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { /*JDK動態代理測試類 * */
    public static void main(String[] args) { Car car=new Car(); //InvocationHandler是一個接口,接口中定義了一個方法invoke。要想實現JDK動態代理, //代理類必須繼承這個接口
        InvocationHandler h=new TimeHandler(car);//         Class cls=car.getClass();//獲取類對象,以便獲取類加載器,以及獲取類的接口
        
        /* * newProxyInstance返回代理類的實例,返回後的代理類能夠看成被代理類使用 * (可以使用被代理類的接口中聲明過的方法) * loader:類加載器 * interfaces:實現接口 * h:InvocationHandler * */ Moveable m=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h); m.move(); } }

測試結果:設計

汽車開始形式.... 汽車行駛中.... 汽車結束行駛...汽車形式時間:863毫秒

 

代理模式-動態代理

因此動態代理是這樣一種Class:

  • 他在運行時候產生了的Class
  • 該class須要實現一組interface
  • 使用動態代理類時,必須實現InvocationHandler接口

動態代理實現步驟

1,建立一個實現InvocationHandler的類,他必須實現Invoke方法

2,建立被代理的類以及接口

3,調用Proxy的靜態方法,建立一個代理類:

newProxyInstance(ClassLoader,class[] interfaces,InvocationHandler  h)。

4,經過代理調用方法。

 

jdk動態代理只能實現了接口的類。

 

jdk動態代理理解:

看完這個例子,個人最大疑惑是,返回代理類實例,執行方法的時候,調用的是被代理類中含有的方法,那麼咱們代理類中重寫的invoke方法,並無調用,

那這個方法有什麼用,可是真正執行被代理類裏面的方法的時候,卻好像又確實執行了invoke方法,那麼到底時候用到了invoke這個方法呢

 

這裏看了一下這篇博客的解釋,稍微能理解一下這種動態代理的處理執行過程:https://juejin.im/post/5a99048a6fb9a028d5668e62。

在靜態代理部分,咱們在代理類中傳入了被代理對象。但是,使用newProxyInstance生成動態代理對象的時候,咱們竟然再也不須要傳入被代理對象了
咱們傳入了的實際對象是InvocationHandler實現類的實例,這看起來有點像生成了InvocationHandler的代理對象,
在動態生成的代理類的任意方法中都會間接調用InvocationHandler->invoke(proxy, method, args)方法。 其實的確是這樣。TimeProxy真正代理的對象就是InvocationHandler,不過這裏設計的巧妙之處在於,InvocationHandler是一個接口,
真正的實現由用戶指定。另外,在每個方法執行的時候,invoke方法都會被調用 ,這個時候若是你須要對某個方法進行自定義邏輯處理,
能夠根據method的特徵信息進行判斷分別處理

 

看完這段文字,再看這個方法的執行:

Car car=new Car(); InvocationHandler h=new TimeHandler(car); Class cls=car.getClass(); Moveable m=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);

總結步驟:

  • InvocationHandler:這個接口主要用於自定義代理邏輯處理
  • 爲了完成對被代理對象的方法攔截,咱們須要在InvocationHandler對象中傳入被代理對象實例。
  • Proxy->newProxyInstance用於生成代理對象。
  • newProxyInstance的三個參數:
  • 前兩個參數與被代理類的Class有關(被代理類的類加載器,被代理類的接口)。
  • 最後一個參數傳入代理類InvocationHandler的實例。

 

想象一下,到此爲止,若是咱們還須要對其它任意對象進行代理,是否還須要改動newProxyInstance方法的源碼,答案是:徹底不須要!

只要你在newProxyInstance方法中指定代理須要實現的接口指定用於自定義處理的InvocationHandler對象

整個代理的邏輯處理都在你自定義的InvocationHandler實現類中進行處理。

至此,而咱們終於能夠從不斷地寫代理類用於實現自定義邏輯的重複工做中解放出來了,今後須要作什麼,交給InvocationHandler。

 

答疑解惑

invoke方法的第一個參數proxy到底有什麼做用?

1. 可使用反射獲取代理對象的信息(也就是proxy.getClass().getName(),輸出可得:com.sun.proxy.$Proxy0)。
2. 能夠將代理對象返回以進行連續調用,這就是proxy存在的目的。由於this並非代理對象,指的是被代理對象實例(如例子中的car),

proxy指的是被代理對象。這個問題其實也好理解,若是你的接口中有方法須要返回自身,若是在invoke中沒有傳入這個參數,將致使實例沒法正常返回。

在這種場景中,proxy的用途就表現出來了。簡單來講,這其實就是最近很是火的鏈式編程的一種應用實現。

動態代理到底有什麼用?

學習任何一門技術,必定要問一問本身,這到底有什麼用。其實,在這篇文章的講解過程當中,咱們已經說出了它的主要用途。你發現沒,使用動態代理咱們竟然能夠在不改變源碼的狀況下,直接在方法中插入自定義邏輯。這有點不太符合咱們的一條線走到底的編程邏輯,這種編程模型有一個專業名稱叫 AOP。所謂的AOP,就像刀同樣,抓住時機,趁機插入。

相關文章
相關標籤/搜索