代理模式-JDK動態代理

對比靜態代理

靜態代理:是指在程序運行前就已經定義好了目標類的代理類。代理類與目標類的代理關係在程序運行之前就確立了。

動態代理:是指程序在整個運行過程中根本就不存在目標類的代理類,目標對象的代理對象只是代理生成工具(如代理工廠類)在程序運行時由JVM根據反射等機制動態生成的。代理對象與目標對象的代理關係在程序運行時才確立。

概念

動態代理類類似與普通當事人與聘請的律師間的關係。律師是在“官司”發生後,才由當事人聘請的。即代理關係是在“官司”發生後才確立的。

動態代理的實現方式常用的有兩種:使用JDK的Proxy,與通過CGLIB生產代理。

分析

通過JDK的java.lang.relect.Proxy類實現動態代理,會使用其靜態方法newProxyInstance(),依據目標對象、業務接口及增強邏輯三者,自動生成一個動態代理對象。

public static Object newProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)

loader:目標類的類加載器,通過目標對象的反射可獲取

interfaces:目標類實現的接口數組,通過目標對象的反射可獲取

handler:業務增強邏輯,需要再定義

InvocationHandler 是個接口,其具體介紹如下:實現了InvocationHandler 接口的類用於加強目標類的主業務邏輯。這個接口中有一個方法invoke(),具體加強的代理邏輯就是定義在該方法中的。程序調用主業務邏輯時,會自動調用invoke()方法。

invoke()方法的介紹如下:

public Object invoke(Object proxy, Method method, Object[] args)

proxy:代表生成的代理對象

method:代表目標方法

args:代表目標方法的參數

由於該方法是由代理對象自動調用的,所以這三個參數的值不用開發人員給出。

第二個參數爲Method類對象,該類有一個方法也叫invoke(),可以調用目標類目標的方法。這兩個invoke()方法,雖然同名,但無關。

public Object invoke(Object obj, Object... args)

obj:表示目標對象

args:表示目標方法參數,就是其上一層invoke方法的第三個參數

該方法的作用是:調用執行obj對象所屬類的方法,這個方法由其調用者Method對象確定。

在代碼中,一般寫法爲method.invoke(target, args);

其中,method爲上一層invoke方法的第二個參數。這樣,即可調用了目標類的目標方法。

大家單看分析還不夠清晰,下面看一下 代理實現與解析

(1)定義業務接口GameService其中含有抽象方法gameStart()

public interface GameService {

    //主業務邏輯

    void gameStart();

}

(2)定義目標類GameServiceImpl,該類實現了業務接口。在對接口方法的實現上,只實現主業務邏輯。這個方法稱爲目標方法。

public class GameServiceImpl implements GameService {

    /**

     * 目標方法

     */

    @Override

    public void gameStart() {

        System.out.println("這裏調用Dao層等,完成gameStart()");

    }

}

(3)定義主業務增強邏輯類JdkDynamic,該類需實現接口InvocationHandler。在該類中定義一個Object類型的成員變量,還要定義一個帶參的構造器,這個參數爲Object對象。目的是,將目標對象引入該類,以便通過反射調用目標方法。

當然,也可以將這個屬性定義爲GameService接口類型,但最好不要這樣做,最好將其定義爲Object。因爲,這樣這個主業務增強邏輯可以使用於本項目中的任何類型的目標類,而不僅僅拘泥於一個類。

/*

 * 定義代理類,與目標類實現相同的業務接口,增強邏輯

 */

public class JdkDynamic implements InvocationHandler {

    //聲明業務接口對象

    private Object target;

    public JdkDynamic(){}
        
    public JdkDynamic(Object target){

        this.target = target;

    }

    /* 代理方法,實現對目標方法的功能增強 */

    @Override

    public Object invoke(Object proxy, Method method, Object[] args)

            throws Throwable {

        // 增加主業務邏輯代碼

        System.out.println("對用戶進行身份驗證");

        // 無論主業務方法有無參數,有無返回值,下面的方法均可兼顧到

        return method.invoke(target, args);

    }

}

(4)定義客戶類Client。客戶類中主要語句有三句:

A、定義目標對象。在生成代理對象時會需要目標對象對其初始化。

B、定義代理對象。需要注意的是,代理類Proxy會通過反射機制,自動實現GameService接口。代理對象需要使用目標對象對其進行初始化。

C、代理對象調用主業務方法。

    public static void main(String[] args) throws IOException {

        //定義目標對象

        GameService target = new GameServiceImpl();

        //創建代理對象,並使用目標對象初始化它

        GameService service = (GameService) Proxy.newProxyInstance(

        target.getClass().getClassLoader(), // 獲取目標對象的類加載器

        target.getClass().getInterfaces(), // 獲取目標類失效的所有接口

        new JdkDynamic(target)); // 增強業務邏輯

        //此時執行的內容,就是對目標對象增加過的內容

        service.gameStart();


        //將JDK動態代理生成的代理類,通過流的形式保存到磁盤中

        System.out.println(service.getClass()); // class com.sun.proxy.$Proxy0

        /**

         * 上面生成的代理對象字節碼 com.sun.proxy.$Proxy0 是在內存中的

         * 這裏將其對象寫到文件中,通過反編譯查看

         */

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{GameService.class});

        FileOutputStream fos = new FileOutputStream("D://code/$Proxy0.class");

fos.write(bytes);

fos.flush();

        System.out.println("文件寫入成功");


    }

(5)將JDK動態生成的代理類,通過流的形式保存到磁盤中,使用反編譯工具查看:

import com.proxyDemo.jdkProxy.service.GameService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
  implements GameService
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void gameStart()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.proxyDemo.jdkProxy.service.GameService").getMethod("gameStart", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
    }
    throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
}

分析JDK動態生成的代理類

1.生成的代理類繼承Proxy類並實現業務接口,並生成了被代理類的全部方法,包括toString、equals等等所有方法
2.Java是單繼承的,代理類實現了【需要被代理類的實現接口】以供我們可以將其強轉;
3.這裏生成的代理類繼承自Proxy,在Proxy中有這麼一段代碼:protected InvocationHandler h;
這是在生成代理類的時候傳入的InvocationHandler的實現,【開發的代理增強代碼(實現InvocationHandler重寫invoke()方法)】都寫在invoke()方法中,最終代理類執行代理也是通過調用父類Proxy中的InvocationHandler接口的invoke()方法;

在調用【被代理】方法時,代理類通過調用生成的同名方法然後調用super.h.invoke()方法,最終就到了我們實現InvocationHandler時所寫的invoke()增強方法;在此時,method.invoke()才真正通過反射調用了我們自身所寫的方法

可以看一下這個鏈接下JDK是如何動態生成代理類的代理類是通過Proxy類的ProxyClassFactory工廠生成的

希望對你有幫助,祝你有一個好心情,加油!

若有錯誤、不全、可優化的點,歡迎糾正與補充!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章