最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 動態(tài)代理深入理解

 更新時間:2017年03月16日 08:53:32   投稿:lqh  
這篇文章主要介紹了Java 動態(tài)代理深入理解的相關資料,需要的朋友可以參考下

要想了解Java動態(tài)代理,首先要了解什么叫做代理,熟悉設計模式的朋友一定知道在Gof總結的23種設計模式中,有一種叫做代理(Proxy)的對象結構型模式,動態(tài)代理中的代理,指的就是這種設計模式。

在我看來所謂的代理模式,和23種設計模式中的“裝飾模式”是一個東西。23種設計模式中將它們作為兩種模式,網上也有些文章講這兩種模式的異同,從細節(jié)來看,確實可以人為地區(qū)分這兩種模式,但是抽象到一定高度后,我認為這兩種模式是完全一樣的。因此學會了代理模式,也就同時掌握了裝飾模式。

代理模式

代理模式簡單來說,就是對一個對象進行包裝,包裝后生成的對象具有和原對象一樣的方法列表,但是每個方法都可以是被包裝過的。

靜態(tài)代理

讓我們先來看一段代碼:

package common;

public class Test {
  static interface Subject{
    void sayHi();
    void sayHello();
  }

  static class SubjectImpl implements Subject{

    @Override
    public void sayHi() {
      System.out.println("hi");
    }

    @Override
    public void sayHello() {
      System.out.println("hello");
    }
  }

  static class SubjectImplProxy implements Subject{
    private Subject target;

    public SubjectImplProxy(Subject target) {
      this.target=target;
    }

    @Override
    public void sayHi() {
      System.out.print("say:");
      target.sayHi();
    }

    @Override
    public void sayHello() {
      System.out.print("say:");
      target.sayHello();
    }
  }

  public static void main(String[] args) {
    Subject subject=new SubjectImpl();
    Subject subjectProxy=new SubjectImplProxy(subject);
    subjectProxy.sayHi();
    subjectProxy.sayHello();
  }
}

這段代碼中首先定義了一個Subject接口,接口中有兩個方法。

然后定義了SubjectImpl類實現Subject接口并實現其中的兩個方法,到這里肯定是沒問題的。

現在再定義一個SubjuectImplProxy類,也實現Subject接口。這個SubjectImplProxy類的作用是包裝SubjectImpl類的實例,它的內部定義一個變量target來保存一個SubjectImpl的實例。SubjectImplProxy也實現了接口規(guī)定的兩個方法,并且在它的實現版本中,都調用了SubjectImpl的實現,但是又添加了自己的處理邏輯。

相信這段代碼不難理解,它通過對SubjectImpl進行包裝,達到了給輸出內容添加前綴的功能。這種代理方式叫做靜態(tài)代理。

動態(tài)代理

從上面的演示中我們不難看出靜態(tài)代理的缺點:我們對SubjectImpl的兩個方法,是進行的相同的包裝,但是卻要在SubjectImplProxy里把相同的包裝邏輯寫兩次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必須要添加新的實現,盡管SubjectImplProxy對所有方法的包裝可能都是一樣的。

下面我把上面例子的靜態(tài)代理改成動態(tài)代理,我們來看一下區(qū)別:

package common;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {
  static interface Subject{
    void sayHi();
    void sayHello();
  }

  static class SubjectImpl implements Subject{

    @Override
    public void sayHi() {
      System.out.println("hi");
    }

    @Override
    public void sayHello() {
      System.out.println("hello");
    }
  }

  static class ProxyInvocationHandler implements InvocationHandler{
    private Subject target;
    public ProxyInvocationHandler(Subject target) {
      this.target=target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.print("say:");
      return method.invoke(target, args);
    }

  }

  public static void main(String[] args) {
    Subject subject=new SubjectImpl();
    Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
    subjectProxy.sayHi();
    subjectProxy.sayHello();

  }
}

只看main方法的話,只有第二行和之前的靜態(tài)代理不同,同樣是生成一個subjectProxy代理對象,只是生成的代碼不同了。靜態(tài)代理是直接new 一個SubjectImplProxy的實例,而動態(tài)代理則調用了java.lang.reflect.Proxy.newProxyInstance()方法,我們來看一下這個方法的源碼:

 

  public static Object newProxyInstance(ClassLoader loader,
                     Class<?>[] interfaces,
                     InvocationHandler h)
    throws IllegalArgumentException
  {
    if (h == null) {
      throw new NullPointerException();
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass(loader, interfaces);  //獲取代理類的Class

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
      Constructor cons = cl.getConstructor(constructorParams);  //constructorParams是寫死的:{ InvocationHandler.class },上邊返回的代理類Class一定是extends Proxy的,而Proxy有一個參數為InvocationHandler的構造函數
      return cons.newInstance(new Object[] { h });  //這里通過構造函數將我們自己定義的InvocationHandler的子類傳到代理類的實例里,當我們調用代理類的任何方法時,實際上都會調用我們定義的InvocationHandler子類重寫的invoke()函數
    } catch (NoSuchMethodException e) {
      throw new InternalError(e.toString());
    } catch (IllegalAccessException e) {
      throw new InternalError(e.toString());
    } catch (InstantiationException e) {
      throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
      throw new InternalError(e.toString());
    }
  }

上面的 Class<?> cl = getProxyClass(loader, interfaces);  調用的getProxyClass方法:

public static Class<?> getProxyClass(ClassLoader loader,
                     Class<?>... interfaces)
    throws IllegalArgumentException
  {
    if (interfaces.length > 65535) {  //因為在class文件中,一個類保存的接口數量是用2個字節(jié)來表示的,因此java中一個類最多可以實現65535個接口
      throw new IllegalArgumentException("interface limit exceeded");
    }

    Class<?> proxyClass = null;

    /* collect interface names to use as key for proxy class cache */
    String[] interfaceNames = new String[interfaces.length];

    // for detecting duplicates
    Set<Class<?>> interfaceSet = new HashSet<>();
     //驗證interfaces里的接口是否能被類加載器加載,是否是接口,是否有重復的 
    for (int i = 0; i < interfaces.length; i++) {
      /*
       * Verify that the class loader resolves the name of this
       * interface to the same Class object.
       */
      String interfaceName = interfaces[i].getName();
      Class<?> interfaceClass = null;
      try {
        interfaceClass = Class.forName(interfaceName, false, loader);
      } catch (ClassNotFoundException e) {
      }
      if (interfaceClass != interfaces[i]) {
        throw new IllegalArgumentException(
          interfaces[i] + " is not visible from class loader");
      }

      /*
       * Verify that the Class object actually represents an
       * interface.
       */
      if (!interfaceClass.isInterface()) {
        throw new IllegalArgumentException(
          interfaceClass.getName() + " is not an interface");
      }

      /*
       * Verify that this interface is not a duplicate.
       */
      if (interfaceSet.contains(interfaceClass)) {
        throw new IllegalArgumentException(
          "repeated interface: " + interfaceClass.getName());
      }
      interfaceSet.add(interfaceClass);

      interfaceNames[i] = interfaceName;
    }

    /*
     * Using string representations of the proxy interfaces as
     * keys in the proxy class cache (instead of their Class
     * objects) is sufficient because we require the proxy
     * interfaces to be resolvable by name through the supplied
     * class loader, and it has the advantage that using a string
     * representation of a class makes for an implicit weak
     * reference to the class.
     */
    List<String> key = Arrays.asList(interfaceNames);  //使用interfaces列表作為key緩存在cache里,也就是實現了相同interfaces的代理類只會創(chuàng)建加載一次

    /*
     * Find or create the proxy class cache for the class loader.
     */
    Map<List<String>, Object> cache;
    synchronized (loaderToCache) {
      cache = loaderToCache.get(loader);
      if (cache == null) {
        cache = new HashMap<>();
        loaderToCache.put(loader, cache);
      }
      /*
       * This mapping will remain valid for the duration of this
       * method, without further synchronization, because the mapping
       * will only be removed if the class loader becomes unreachable.
       */
    }

    /*
     * Look up the list of interfaces in the proxy class cache using
     * the key. This lookup will result in one of three possible
     * kinds of values:
     *   null, if there is currently no proxy class for the list of
     *     interfaces in the class loader,
     *   the pendingGenerationMarker object, if a proxy class for the
     *     list of interfaces is currently being generated,
     *   or a weak reference to a Class object, if a proxy class for
     *     the list of interfaces has already been generated.
     */
     //看看緩存里有沒有,如果有就直接取出來然后return,否則判斷根據pendingGenerationMarker判斷是否有其它線程正在生成當前的代理類,如果有則cache.wait()等待,如果沒有則創(chuàng)建。
    synchronized (cache) {
      /*
       * Note that we need not worry about reaping the cache for
       * entries with cleared weak references because if a proxy class
       * has been garbage collected, its class loader will have been
       * garbage collected as well, so the entire cache will be reaped
       * from the loaderToCache map.
       */
      do {
        Object value = cache.get(key);
        if (value instanceof Reference) {
          proxyClass = (Class<?>) ((Reference) value).get();
        }
        if (proxyClass != null) {
          // proxy class already generated: return it
          return proxyClass;
        } else if (value == pendingGenerationMarker) {
          // proxy class being generated: wait for it
          try {
            cache.wait();
          } catch (InterruptedException e) {
            /*
             * The class generation that we are waiting for should
             * take a small, bounded time, so we can safely ignore
             * thread interrupts here.
             */
          }
          continue;
        } else {
          /*
           * No proxy class for this list of interfaces has been
           * generated or is being generated, so we will go and
           * generate it now. Mark it as pending generation.
           */
          cache.put(key, pendingGenerationMarker);
          break;
        }
      } while (true);
    }
     //確認要生成的代理類所屬的包,如果interfaces里所有接口都是public的,代理類所屬包就是默認包;如果有interface不是public,那么所有不是public的interface必須在一個包里否則報錯。
    try {
      String proxyPkg = null;   // package to define proxy class in

      /*
       * Record the package of a non-public proxy interface so that the
       * proxy class will be defined in the same package. Verify that
       * all non-public proxy interfaces are in the same package.
       */
      for (int i = 0; i < interfaces.length; i++) {
        int flags = interfaces[i].getModifiers();
        if (!Modifier.isPublic(flags)) {
          String name = interfaces[i].getName();
          int n = name.lastIndexOf('.');
          String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
          if (proxyPkg == null) {
            proxyPkg = pkg;
          } else if (!pkg.equals(proxyPkg)) {
            throw new IllegalArgumentException(
              "non-public interfaces from different packages");
          }
        }
      }

      if (proxyPkg == null) {   // if no non-public proxy interfaces,
        proxyPkg = "";     // use the unnamed package
      }

      {
        /*
         * Choose a name for the proxy class to generate.
         */
        long num;
        synchronized (nextUniqueNumberLock) {
          num = nextUniqueNumber++;
        }
        String proxyName = proxyPkg + proxyClassNamePrefix + num;  //生成代理類的名字,proxyPkg是上面確定下來的代理類所在的包名,proxyClassNamePrefix是寫死的字符串“$Proxy”,num是一個全局唯一的long型數字,從0開始累積,每次生成新的代理類就+1,從這里也能看出生成的動態(tài)代理類的數量不能超過Long.maxValue
        /*
         * Verify that the class loader hasn't already
         * defined a class with the chosen name.
         */

        /*
         * Generate the specified proxy class.
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
          proxyName, interfaces);  //生成一個以proxyName為類名的,實現了Interfaces里所有接口的類的字節(jié)碼
        try {
          proxyClass = defineClass0(loader, proxyName,
            proxyClassFile, 0, proxyClassFile.length);  //加載生成的類
        } catch (ClassFormatError e) {
          /*
           * A ClassFormatError here means that (barring bugs in the
           * proxy class generation code) there was some other
           * invalid aspect of the arguments supplied to the proxy
           * class creation (such as virtual machine limitations
           * exceeded).
           */
          throw new IllegalArgumentException(e.toString());
        }
      }
      // add to set of all generated proxy classes, for isProxyClass
      proxyClasses.put(proxyClass, null);

    } finally {
      /*
       * We must clean up the "pending generation" state of the proxy
       * class cache entry somehow. If a proxy class was successfully
       * generated, store it in the cache (with a weak reference);
       * otherwise, remove the reserved entry. In all cases, notify
       * all waiters on reserved entries in this cache.
       */
       //創(chuàng)建成功,則將cache中該key的pendingGenerationMarker替換為實際的代理類的弱引用,否則也要清除pendingGenerationMarker標記;不管是否成功,都要執(zhí)行cache.notifyAll(),讓其它要創(chuàng)建相同代理類并且執(zhí)行了cache.wait()的線程恢復執(zhí)行。
      synchronized (cache) {
        if (proxyClass != null) {
          cache.put(key, new WeakReference<Class<?>>(proxyClass));
        } else {
          cache.remove(key);
        }
        cache.notifyAll();
      }
    }
    return proxyClass; //最后返回代理類Class
  }

到這里,我們已經把動態(tài)代理的java源代碼都解析完了,現在思路就很清晰了:

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法簡單來說執(zhí)行了以下操作:

1.生成一個實現了參數interfaces里所有接口且繼承了Proxy的代理類的字節(jié)碼,然后用參數里的classLoader加載這個代理類。

2.使用代理類父類的構造函數 Proxy(InvocationHandler h)來創(chuàng)造一個代理類的實例,將我們自定義的InvocationHandler的子類傳入。

3.返回這個代理類實例,因為我們構造的代理類實現了interfaces(也就是我們程序中傳入的subject.getClass().getInterfaces())里的所有接口,因此返回的代理類可以強轉成Subject類型來調用接口中定義的方法。

現在我們知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功強轉成Subject類型來調用接口中定義的方法了,那么在調用方法后,代理類實例怎么進行處理的呢,這就需要看一下代理類的源碼了。但是代理類是程序動態(tài)生成字節(jié)碼加載的,怎么看源碼呢?沒關系,可以在main方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”),這樣就會把生成的代理類Class文件保存在本地磁盤上,然后再反編譯可以得到代理類的源碼:

package common;

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 Test.Subject
{
 private static Method m4;
 private static Method m1;
 private static Method m3;
 private static Method m0;
 private static Method m2;

 static
 {
   try {
     m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
     m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
     m3 = Class.forName("Test$Subject").getMethod("sayHi", 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]);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
 }

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

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

 public final boolean equals(Object paramObject)
 {
  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 sayHi()
 {
  try
  {
   this.h.invoke(this, m3, null);
   return;
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }

 public final int hashCode()
 {
  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()
 {
  try
  {
   return (String)this.h.invoke(this, m2, null);
  }
  catch (RuntimeException localRuntimeException)
  {
   throw localRuntimeException;
  }
  catch (Throwable localThrowable)
  {
    throw new UndeclaredThrowableException(localThrowable);
  }
 }
}

我們可以看到代理類內部實現比較簡單,在調用每個代理類每個方法的時候,都用反射去調h的invoke方法(也就是我們自定義的InvocationHandler的子類中重寫的invoke方法),用參數傳遞了代理類實例、接口方法、調用參數列表,這樣我們在重寫的invoke方法中就可以實現對所有方法的統(tǒng)一包裝了。

總結

動態(tài)代理相對于靜態(tài)代理在使用上的優(yōu)點主要是能夠對一個對象的所有方法進行統(tǒng)一包裝,而且后期被代理的類添加方法的時候動態(tài)代理類不需要改動。

缺點是要求被代理的類必須實現了接口,因為動態(tài)代理類在實現的時候繼承了Proxy類,java不支持多繼承,因此動態(tài)代理類只能根據接口來定義方法。

最后動態(tài)代理之所以叫做動態(tài)代理是因為java在實現動態(tài)代理的時候,動態(tài)代理類是在運行時動態(tài)生成和加載的,相對的,靜態(tài)代理類和其他普通類一下,在類加載階段就加載了。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關文章

  • java集合遍歷的幾種方式總結及詳細比較

    java集合遍歷的幾種方式總結及詳細比較

    下面小編就為大家?guī)硪黄猨ava集合遍歷的幾種方式總結及詳細比較。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Java中遍歷Map的多種方法示例及優(yōu)缺點總結

    Java中遍歷Map的多種方法示例及優(yōu)缺點總結

    在java中遍歷Map有不少的方法,下面這篇文章主要給大家介紹了關于Java中遍歷Map的多種方法,以及各種方法的優(yōu)缺點總結,文中通過示例代碼介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-07-07
  • Presto自定義函數@SqlNullable引發(fā)問題詳解

    Presto自定義函數@SqlNullable引發(fā)問題詳解

    這篇文章主要為大家介紹了Presto自定義函數@SqlNullable引發(fā)問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • Spring Boot搭建文件上傳服務的方法

    Spring Boot搭建文件上傳服務的方法

    這篇文章主要為大家詳細介紹了Spring Boot搭建文件上傳服務的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Scala中的mkString的具體使用方法

    Scala中的mkString的具體使用方法

    這篇文章主要介紹了Scala中的mkString的具體方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-06-06
  • java并發(fā)包JUC同步器框架AQS框架原文翻譯

    java并發(fā)包JUC同步器框架AQS框架原文翻譯

    發(fā)現了一篇JDK作者的論文《The?java.util.concurrent?Synchronizer?Framework》主要描述了作者對AbstractQueuedSynchronizer同步器框架的設計和實現。權威性毋庸置疑!自然需要拜讀一下,配上中文翻譯,希望大家能有所收獲
    2022-02-02
  • 淺談Java中IO和NIO的本質和區(qū)別

    淺談Java中IO和NIO的本質和區(qū)別

    IO的全稱是input output,是java程序跟外部世界交流的橋梁,IO指的是java.io包中的所有類,他們是從java1.0開始就存在的。NIO叫做new IO,是在java1.4中引入的新一代IO。IO的本質是什么呢?它和NIO有什么區(qū)別呢?我們該怎么學習IO和NIO呢?看完這篇文章一切都有答案。
    2021-06-06
  • idea2020.3.3集成maven及遇到的坑(推薦)

    idea2020.3.3集成maven及遇到的坑(推薦)

    這篇文章主要介紹了idea2020.3.3集成maven的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • eclipse的git插件安裝、配置與使用詳解

    eclipse的git插件安裝、配置與使用詳解

    這篇文章主要介紹了eclipse的git插件安裝、配置與使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 關于bigDecimal類的精度保留方法

    關于bigDecimal類的精度保留方法

    這篇文章主要介紹了關于bigDecimal類的精度保留方法,計算機存儲的浮點數受存儲bit位數影響,只能保證一定范圍內精準,超過bit范圍的只能取近似值,Java使用java.math.BigDecimal專門處理小數精度,需要的朋友可以參考下
    2023-07-07

最新評論

靖宇县| 孝昌县| 杭州市| 大理市| 四川省| 灌云县| 班玛县| 独山县| 乌鲁木齐市| 烟台市| 仲巴县| 徐汇区| 舟曲县| 靖安县| 彰化县| 乐清市| 三原县| 襄垣县| 江门市| 乌拉特前旗| 临高县| 霸州市| 洛阳市| 石渠县| 正宁县| 武乡县| 特克斯县| 惠水县| 铜山县| 鸡泽县| 武宁县| 开阳县| 五河县| 陈巴尔虎旗| 福安市| 内黄县| 积石山| 雅江县| 长岭县| 广州市| 宁波市|