Jdk動態代理原理解析


  • title: Jdk動態代理原理解析
  • tags:代理
  • categories:筆記
  • date: 2017-06-14 9:48:05

動態代理這個知識點,也是我們開發過程中非常容易遇到。特別的是在一些框架中,為了滿足軟件開發的開閉原則,以及增強框架自身的靈活拓展功能。在底層就會為那些特定的目標類或者接口實現類進行渲染與自定義功能操作。就如spring框架中的aop,底層也是通過動態代理來實現的,所以,就像看看jdk自身的動態代理是如
何實現的,整個過程是如何流動的。


image

什么是動態代理,有什么優勢?

動態,指的是代理類實在程序運行時創建的,而不是在程序運行前手動編碼來定義代理類的。這些動態代理類是在運行時候根據我們在JAVA代碼中
的“指示”動態生成的。相比于靜態代理,動態代理的優勢在于可以很方便的對代理類的函數進行統一處理,而不用修改每個代理類的函數。
有點類似于AOP的動態切面編程,為程序以供一些基礎公用的實現,而不用分別針對每個實際類的方法進行前后環繞處理。有很多框架都是基于反射和動態代理基礎上進行框架能力擴展,并實現JAVASE平臺上基礎接口或者繼承類進而實現與JAVASE平臺的耦合。

動態代理使用場景,有哪些類別?

動態代理的使用方式呢,主要就是分為兩種:一種是基于接口的代理;另一種則是基于類的代理。基于接口的代理,就是jdk自帶的動態代理規則的實現方式,后者則是基于一些字節類增強的類代理,如cglib,javassist等。

但是,動態代理實際的操作對象,都是在目標類的基礎上,生成一個具有代理目的,增強功能的新的代理proxy字節碼類。都是在復合class字節碼的規范下,對class字節文件內的內容進行操作,最后將生成的字節類對應的代理類,通過類加載器加載到JVM中,進行使用。

● JDK自帶的基于接口Interface的代理
● 三方CGLIB,JAVASSIST等字節碼處理的類庫,是基于Class類來實現代理

動態代理原理分析

說完了,動態代理的概念和大致的種類劃分,現在就基于jdk自己的基于接口的動態代理來進行分析,通過實際的代碼樣例,逐步分析每個過程,到最后目標類的擴展完成。

動態代理使用

【1】jdk代理代碼樣例:
結合最上面的,代理結構圖,可以知道。jdk代理最主要的就是三個類:目標接口,目標類(實現了目標接口),擴展處理器InvocationHandler類。

//目標接口,對應ITraget
public interface Subject {
    void hello(String str);
}

//目標類,對應Target
public class RealSubject implements Subject{
    public void hello(String str) {
        System.out.println("hello" + str);
    }
}

//InvocationHandler增強處理器接口實現類
//方法調用句柄invoke方法內部就是代理類的擴展點
public class DynamicProxy implements InvocationHandler{

    private Object target;//反射代理目標類(被代理,解耦的目標類)
    
    //可以通過構造器動態設置被代理目標類,以便于調用指定方法
    public DynamicProxy(Object subject){
        this.target = subject;
    }
    
    //代理過程中的擴展點
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("brfore call specific method >>" + method.getName());
        Object result = method.invoke(target, args);//MethodAccessor.invoke()
        System.out.println("after call specific method>>" + method.getName());
        return result;
    }
}

【2】客戶端調用:
在定義完動態代理幾種基本類型之后,就可以在客戶端中進行代理實現。為了更清楚說明這個動態代理的過程,可以分為以下兩種情形來進行使用分析:
(1)冗余型:過程清新,代碼冗余

    @Test
    public void t() throws Exception{
        Subject realSubject = new RealSubject();
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 
        //1.0 獲取代理類的類對象,主要設置相同的ClassLoader去加載目標類實現的接口Subject類
        Class<?> proxyClass = Proxy.getProxyClass(Client.class.getClassLoader(), new Class[]{Subject.class});
        //2.0 得到代理類后,就可以通過代理類的處理器句柄來得到構造器
        final Constructor<?> con = proxyClass.getConstructor(InvocationHandler.class);
        //3.0 獲取具體執行方法的句柄處理器,目的通過構造器傳入被代理目標類對象,注入到代理類處理器句柄中進行代理調用
        final InvocationHandler handler = new DynamicProxy(realSubject);
        //4.0 通過構造器創建代理類對象
        Subject subject = (Subject)con.newInstance(handler);
        
        //5.0 最后調用方法
        subject.hello("proxy");

(2)簡潔型:過程不是很清新,但是代碼簡潔
這種使用方式,也是我們經常看到的。

    //設置生成代理類文件到本地
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 
    Subject subject2 = (Subject)Proxy.newProxyInstance(Client.class.getClassLoader(),
                         new Class[]{Subject.class}, new DynamicProxy(new RealSubject()));
    //調用代理類方法
    subject2.hello("proxy");

【3】使用分析:
通過上述代碼可以知道,jdk中的接口代理有幾個重要的點:

  1. 最后方法的調用一定是聲明在接口的方法。只是具體動態方法調用的時候,執行的是接口實現子類中方法。
  2. 方法的調用點一定是在InvocationHandler接口或者其子類的invoke方法中,并且該接口中存在目標類的對象(依賴),這里也是代理類的拓展點。
  3. 方法調用的對象,一定是通過構造器形式來創建出來的。

在按照規定,正確使用了proxy動態代理之后,會思考:為什么jdk的代理一定是基于接口實現的呢?
這里,可以先說說分析后的想法:

因為在動態代理過程中,會生成對應的代理類形如$Proxy0,$Proxy1....</font>這樣的匿名類,這些類都是繼承java.lang.reflect.Proxy類和實現了我們傳入
的接口,形如:public final class $Proxy0 extends Proxy implements Subject .所以,通過Proxy.newProxyInstance方法返回的就是這個匿名類的實例。通常就通過強制轉換成指定接口,最后就可以調用方法了。【java中不能多重繼承,當代理匿名類實現了jdk中的Proxy.class類的時候,就只能通過實現目標接口的方式來實現拓展

(Subject)Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, new DynamicProxy(new RealSubject()))

若是強制轉換成接口實現子類,形如:

(RealSubject)Proxy.newProxyInstance(Client.class.getClassLoader(), 
new Class[]{Subject.class}, new DynamicProxy(new RealSubject()))

就會報如下錯誤:
java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to proxy.RealSubject。

代理流程分析

就基于上述使用的樣例中的動態代理的代碼,對每個步驟的過程進行分析。看看,每個步驟內部是如何運行的。
因為,整個jdk動態代理過程都與java.lang.reflect.Proxy類有關,就先看看該類中一些重要分屬性字段:


image

生成代理類

然后,整體看看創建代理對象newProxyInstance方法源代碼:

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

        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

結合上述代碼,來講講幾個重要的階段:
【1】傳入目標接口類和處理器InvocationHandler實現類
可以看到客戶端調用newProxyInstance方法,傳入了類加載器,接口類還有句柄處理器Invocationhandler實現類。傳入的這些類都是為下面步驟做基礎的。

(Subject)Proxy.newProxyInstance(Client.class.getClassLoader(),
                            new Class[]{Subject.class}, new DynamicProxy(new RealSubject()))

【2】根據類加載器和目標接口類獲取代理類Class對象:
這部分也是代理類的核心,因為這方法里面包含了代理類的動態創建過程。會生成一個形如$Proxy0...之類的動態代理類,然后JVM會加載這些字節類,得到對應的Class對象,進行緩存。下面看看過程:

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 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<>();

        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");
            }

            ....
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        List<String> key = Arrays.asList(interfaceNames);

        /*
         * 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);
            }
        }

        synchronized (cache) {

            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) {
                    }
                    continue;
                } else {

                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
            String proxyPkg = null;     // package to define proxy class in

            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, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            {
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num;
                ....
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    proxyClass = defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
            
                    throw new IllegalArgumentException(e.toString());
                }
            }
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);

        } finally {
            ....
        }
        return proxyClass;
    }

根據源代碼來逐步分析分析:(最重要的就是生成代理類字節碼$Proxy0...$Proxyn,還有就是進行緩存
(1)首先創建一個字符串數組,用于存放入參interfaces中的所有不同接口的類型。便于后面使用全限定類名字符串創建類實例和緩存處理。

   String[] interfaceNames = new String[interfaces.length],先創建容器存放類字符串名。

(2)循環傳入的多參接口類型:并通過Class.forName()方法反射拿到字面量類對應的Class<?>對象,并對該對象進行同名Class是否相同校驗,判斷該類是否是接口校驗,校驗傳入的接口參數是否有重復等等,最后將校驗過后有效的Class對象的類名給存放到(1)中的interfaceNames變量中。

(3) 在Proxy類中有個loadToCache變量,是用來保存JVM中根據指定ClassLoader加載的所有Class類的緩存信息。類型變量是

Map<ClassLoader,Map<List<String>,Object>>

因為判斷是否是同一個類,需要與將類加載器與類一起判定。

(4)往下,就是通過這些類名,結合類加載器來創建或者查詢指定的Class代理類對象。現將interfaceNames字符數組中類字符串轉化為List<String>,

   List<String> key = Arrays.asList(interfaceNames);

然后創建一個 Map<List<String>, Object>類型的cache變量,用于存放已經校驗后加載過的類字符串,形如:[java.lang.Class, java.util.List, java.util.Map]=java.lang.Object@59b55efc 樣子的代理類,
其中Object主要是用來當做信號量也就是 互斥鎖的。Object位置的值根據不同情況分為以下幾種:
【 Object value = cache.get(key)

4.1. 當value instanceof java.lang.ref.Reference : 說明根據傳入的interfaces接口類,在當前緩存中能找到對應的Class對象,并根據這個key返回Class.
4.2. 當value==pendingGenerationMarker: 說明,JVM正在生成或者加載指定key值類字符串代表的Class對象,這時候不能重新生成代理類,需要等待。cache.wait(),等待當其他創建代理類線程將代理類創建好后,notify提醒并恢復線程。
4.3. 當代理類proxyClass==null 并且value!=pendingGenerationMarker:這時候說明,需要根據類字符串創建Class并加載類了。

(5) 通過一個方法內的代碼塊,創建代理類$Proxy類字節碼,并將生成的類信息加載到cache緩存中:
5.1.通過ProxyGenerator.generateProxyClass( proxyName, interfaces)方法,按照Class文件標準格式結合JNI的defineClass0()方法生成Proxy匿名代理類。

5.2 .將生成的代理類保存到proxyClasses變量中,用于為isProxyClass()方法服務。

5.3.最后,將生成的匿名代理字節類$Proxy0,1...等信息保存到緩沖中: cache.put(key, new WeakReference<Class<?>>(proxyClass)),創建了一個弱引用的代理類。
5.4.返回該代理類對應的Class對象。

那么生成的動態代理類是什么樣呢?
可以在代碼中設置:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),會在項目根目錄生成class字節文件,通過反編譯可以看到:

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

  //這就是為什么需要將InvocationHandler.class出入構造器來查找構造器實例的原理
  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 (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

//這個是我們要調用的目標類中方法
  public final void hello(String paramString)
    throws 
  {
    try
    {
     //實際上可以看到,是調用了我們自定義的InvocationHandler接口實現類的invoke方法。
    // m3這個Method對象,是通過反射獲取的
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

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

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    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("proxy.Subject").getMethod("hello", new Class[] { Class.forName("java.lang.String") });
      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());
    }
  }
}

【3】根據得到的代理類Class對象,通過反射獲取指定構造器類對象,且并未創建對象:

Constructor<?> cons = proxyClass.getConstructor(new Class[]{InvocationHandler.class}) 

為什么要傳入一個InvocationHandler.class類型的類對象到構造器呢去那里找這個構造器呢
其實就是在獲取得到的代理類$Proxy0.class字節碼中,通過反編譯就可以看到:
該類有一個構造器,且其構造器參數就是InvocationHandler。如下:

   public $Proxy0(InvocationHandler paramInvocationHandler) throws 
            {
              super(paramInvocationHandler);
         }
          //其實,也是繼承了Proxy父類的構造器形式。
          protected Proxy(InvocationHandler h) {
            doNewInstanceCheck();
                 this.h = h;
        }

那么,Class.class中的getConstructor(Class<?>... parameterTypes)內部是如何實現的呢,通過以下幾步:

  1. 調用Class類中的方法:getConstructor0(parameterTypes, Member.PUBLIC)
    A. privateGetDeclaredConstructors()獲取所有Public修飾公有的構造器,返回構造器數組
    B. 根據構造器類型,遍歷構造器數組并進行參數對比,找到代理類中所有構造器中參數是parameterTypes的構造器。
    C. 找到匹配的構造器constructor后,調用ReflectionFactory工廠的copyConstructor(constructor)復制構造器。
    D. 調用ReflectAccess類的copyConstructor(Constructor<T> args)方法。
    E. 最后還是要調用Constructor.class中的copy()方法,來對找到的構造器進行復制。完成屬性root和constructorAccessor屬性的賦值。

  2. 返回復制好的新的構造器對象。

【4】根據得到的構造器類創建代理類實例:newInstance(cons,InvocationHandler)</font>
調用入參構造器的newInstance方法,通過反射創建代理類的對象。

  final InvocationHandler ih = h;
  cons.newInstance(new Object[] {h} );

那么,構造器創建實例內部是如何運行的:

  1. 調用Constructor.class的newInstance方法:Constructor.newInstance(Object ... initargs):
    A. 先判斷Constructor共用的constructorAccessor屬性是否為null?
    B. 若為null,則調用acquireConstructorAccessor()方法獲取一個ConstructorAccessor接口實例;若不為空,則調用constructorAccessor.newInstance()方法。

  2. 調用acquireConstructorAccessor()方法:目的為了獲取共用的ConstructorAccessor實現類。
    A. 判斷夫屬性root.getConstructorAccessor()是否為null?
    B. 因為構造器訪問器是共用的。所以若是父類中有,那么直接返回。
    C.若是父類的構造器訪問器為null,則需要調用ReflectionFactory.newConstructorAccessor(this)方法。
    D. 先判斷ReflectionFactory中的noInflation屬性是否為true,默認是false。若是noInflation==true或者則調用MethodAccessorGenerator的generateConstructor()方法,生成一個類名為:“sun.reflect.GeneratedConstructorAccessor”+Num形式的標準類字節,并使用DelegatingClassLoader來加載這個類,并將該GeneratedConstructorAccessorXXX實例對象返回。
    E. 若是noInfation為false,且NativeConstructorAccessorImpl類中的表示該JNI本地方法newInstance0被調用次數小于15次時候,就會返回DelegatingConstructorAccessorImpl代理類,并設置該代理類中delegate代理屬性字段為NativeConstructorAccessorImpl的實例對象。
    F. 當獲取到了ConstructorAccessor實例對象后,就調用該對象的newInstance(initargs)方法:
    在這個最后調用創建實例的方法中,若是多次調用這個方法,實際上是通過代理調用NativeConstructorAccessorImpl.newInstance0()方法,若是該方法調用次數大于15次,就會 如D步驟,生成一個GeneratedConstructorAccessorXXX類實例對象返回,且調用該實例對象的newInstance(initargs)方法。

  3. 得到ConstrcutorAccessor接口實例對象,調用該對象的newInstance(initargs)方法。

代理類方法調用

當通過上面的流程運行過后,就能生成并返回一個動態代理類<font color="red">$Proxy0</font>的實例對象。該代理類內部的源代碼通過上述步驟中,也能看到了。剩下的,就是調用返回的目標接口實現類:$Proxy0類的對象調用接口中聲明中方法,進行功能增強了:

 Subject subject2 = (Subject)Proxy.newProxyInstance(Client.class.getClassLoader(),
                    new Class[]{Subject.class}, new DynamicProxy(new RealSubject()));
 //目標類的代理類方法調用
 subject2.hello("dynamic proxoy");

那么,這個調用的方法實際上如何運行的呢?結合$Proxy0類的源代碼來說明:
【1】首先,在動態代理類$Proxy0中的靜態代碼塊中,通過反射首先獲得被代理目標類中的調用方法方法參數。因為最終的方法調用,還是要通過該反射得到的Method對象進行調用的。</font>

     private static Method m3;
     m3 = Class.forName("proxy.Subject").getMethod("hello", new Class[] { Class.forName("java.lang.String") });

可以看到,是通過反射得到代理目標類中的拓展點方法的Method對象。
【2】調用擴展的方法,內部實際是調用調用處理器InvocationHandler實現類的invoke方法。這就是為什么我們要自定義InvocationHandler實現類,并重寫該接口的invoke方法。并傳入代理目標類this,被擴展方法m3,方法參數等等。

//這個是我們要調用的目標類中方法
  public final void hello(String paramString)
    throws 
  {
    try
    {
     //實際上可以看到,是調用了我們自定義的InvocationHandler接口實現類的invoke方法。
    // m3這個Method對象,是通過反射獲取的
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

可以看到,當我們在調用代理返回的代理對象的hello方法的時候,其實底層調用的是我們自定義InvaotionHandler實現類的invoke方法。其中,變量this.h是那里來的呢?其實,就是通過extends Proxy 繼承java.lang.reflect.Proxy類中的,h的類型就是InvocationHandler,如下:

//java.lang.reflect.Proxy.class
public class Proxy implements java.io.Serializable {

    .....
      /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;
}

可以看到,該字段是protected類型的,就是子類可以訪問并使用到的。
【3】最后就是,自定義InvocationHandler內部invoke方法調用:
我們可控的調用鏈就是到自定義InvocationHandler實現類的invoke方法就終止了,看看我們定義的實現類invoke實現:

public class DynamicProxy implements InvocationHandler{

    private Object target;//反射代理目標類(被代理,解耦的目標類)
    
    //可以通過構造器動態設置被代理目標類,以便于調用指定方法
    public DynamicProxy(Object subject){
        this.target = subject;
    }
    
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("brfore call specific method >>" + method.getName());
        Object result = method.invoke(target, args);//MethodAccessor.invoke()
        System.out.println("after call specific method>>" + method.getName());
        return result;
    }

}

可以看到,通過第二步,通過反射得到的Method m3對象,傳入invoke方法內,最后就是調用的是m3.invoke方法。關于反射方法的invoke方法的內部實現,可以參考:反射代理類加載器的潛在內存使用問題.
總結來說,就是通過Method.copy方法拷貝一個相同的方法,并調用所有方法共享的MethodAccessor對象來實際調用。

所以,Proxy代理類的執行過程:

Proxy.newInstance() --> Constructor.copy() ----Constructor.newInstance() ----->
 $Proxy代理字節類生成 ---> Method.copy()-----> $Proxy.invoke()(目標接口方法調用) 
 ---> InvocationHandler.invoke() ---> Method.invoke()

同時我們一定要記住,通過 Proxy.newProxyInstance 創建的代理對象是在jvm運行時動態生成的一個對象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運行是動態生成的一個對象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最后一個數字表示對象的標號。
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 參數可以在項目根目錄下的com/sun/proxy/目錄下生成$Proxy0,1....代理類字節碼文件。】

另外說說代理有關的Inflation機制
若是想測試看看Inflation機制:
調用次數大于15或者設置noInflation=true,那么就可以看到GeneratedConstructorAccessorXXX和GeneratedMethodAccessorXXX類被JVM加載。
可以通過JVM參數-XX:+TraceClassLoading-Dsun.reflect.noInflation=true來查看JVM加載了這兩種類型的類。(ps: 只有接口實例調用方法才會有GeneratedMethodAccessorXXX)

[Loaded <font color="red">sun.reflect.GeneratedConstructorAccessor1 from JVM_DefineClass]</font>
[Loaded sun.reflect.BootstrapConstructorAccessorImpl from F:\softwares\JDK\jre1.8\lib\rt.jar]
[Loaded sun.misc.URLClassPath from F:\softwares\JDK\jre1.8\lib\rt.jar]
[Loaded sun.net.www.protocol.jar.Handler from F:\softwares\JDK\jre1.8\lib\rt.jar]
[Loaded sun.reflect.GeneratedConstructorAccessor2 from JVM_DefineClass]
.....
[Loaded <font color="red">sun.reflect.GeneratedMethodAccessor1 from JVM_DefineClass]</font>
[Loaded sun.reflect.GeneratedConstructorAccessor4 from JVM_DefineClass]

若是按照常規noInflation=false的時候,就應該調用JNI來通過本地Native實現類來生成和加載類:(且有緩存,通常只是加載一遍那個類)
[Loaded sun.reflect.ReflectionFactory$1 from F:\softwares\JDK\jre1.8\lib\rt.jar]
[Loaded sun.reflect.NativeConstructorAccessorImpl from F:\softwares\JDK\jre1.8\lib\rt.jar]
[Loaded sun.reflect.DelegatingConstructorAccessorImpl from F:\softwares\JDK\jre1.8\lib\rt.jar]
......
[Loaded sun.reflect.NativeMethodAccessorImpl from F:\softwares\JDK\jre1.8\lib\rt.jar]
[Loaded sun.reflect.DelegatingMethodAccessorImpl from F:\softwares\JDK\jre1.8\lib\rt.jar]


完結。

image

今天下雨了,無所思,無所感。
不患寡而患不安,就如被雨水不斷擊打的地面,心里無名的....

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容