Jdk動(dòng)態(tài)代理原理解析


  • title: Jdk動(dòng)態(tài)代理原理解析
  • tags:代理
  • categories:筆記
  • date: 2017-06-14 9:48:05

動(dòng)態(tài)代理這個(gè)知識點(diǎn),也是我們開發(fā)過程中非常容易遇到。特別的是在一些框架中,為了滿足軟件開發(fā)的開閉原則,以及增強(qiáng)框架自身的靈活拓展功能。在底層就會為那些特定的目標(biāo)類或者接口實(shí)現(xiàn)類進(jìn)行渲染與自定義功能操作。就如spring框架中的aop,底層也是通過動(dòng)態(tài)代理來實(shí)現(xiàn)的,所以,就像看看jdk自身的動(dòng)態(tài)代理是如
何實(shí)現(xiàn)的,整個(gè)過程是如何流動(dòng)的。


image

什么是動(dòng)態(tài)代理,有什么優(yōu)勢?

動(dòng)態(tài),指的是代理類實(shí)在程序運(yùn)行時(shí)創(chuàng)建的,而不是在程序運(yùn)行前手動(dòng)編碼來定義代理類的。這些動(dòng)態(tài)代理類是在運(yùn)行時(shí)候根據(jù)我們在JAVA代碼中
的“指示”動(dòng)態(tài)生成的。相比于靜態(tài)代理,動(dòng)態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進(jìn)行統(tǒng)一處理,而不用修改每個(gè)代理類的函數(shù)。
有點(diǎn)類似于AOP的動(dòng)態(tài)切面編程,為程序以供一些基礎(chǔ)公用的實(shí)現(xiàn),而不用分別針對每個(gè)實(shí)際類的方法進(jìn)行前后環(huán)繞處理。有很多框架都是基于反射和動(dòng)態(tài)代理基礎(chǔ)上進(jìn)行框架能力擴(kuò)展,并實(shí)現(xiàn)JAVASE平臺上基礎(chǔ)接口或者繼承類進(jìn)而實(shí)現(xiàn)與JAVASE平臺的耦合。

動(dòng)態(tài)代理使用場景,有哪些類別?

動(dòng)態(tài)代理的使用方式呢,主要就是分為兩種:一種是基于接口的代理;另一種則是基于類的代理?;诮涌诘拇?,就是jdk自帶的動(dòng)態(tài)代理規(guī)則的實(shí)現(xiàn)方式,后者則是基于一些字節(jié)類增強(qiáng)的類代理,如cglib,javassist等。

但是,動(dòng)態(tài)代理實(shí)際的操作對象,都是在目標(biāo)類的基礎(chǔ)上,生成一個(gè)具有代理目的,增強(qiáng)功能的新的代理proxy字節(jié)碼類。都是在復(fù)合class字節(jié)碼的規(guī)范下,對class字節(jié)文件內(nèi)的內(nèi)容進(jìn)行操作,最后將生成的字節(jié)類對應(yīng)的代理類,通過類加載器加載到JVM中,進(jìn)行使用。

● JDK自帶的基于接口Interface的代理
● 三方CGLIB,JAVASSIST等字節(jié)碼處理的類庫,是基于Class類來實(shí)現(xiàn)代理

動(dòng)態(tài)代理原理分析

說完了,動(dòng)態(tài)代理的概念和大致的種類劃分,現(xiàn)在就基于jdk自己的基于接口的動(dòng)態(tài)代理來進(jìn)行分析,通過實(shí)際的代碼樣例,逐步分析每個(gè)過程,到最后目標(biāo)類的擴(kuò)展完成。

動(dòng)態(tài)代理使用

【1】jdk代理代碼樣例:
結(jié)合最上面的,代理結(jié)構(gòu)圖,可以知道。jdk代理最主要的就是三個(gè)類:目標(biāo)接口,目標(biāo)類(實(shí)現(xiàn)了目標(biāo)接口),擴(kuò)展處理器InvocationHandler類。

//目標(biāo)接口,對應(yīng)ITraget
public interface Subject {
    void hello(String str);
}

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

//InvocationHandler增強(qiáng)處理器接口實(shí)現(xiàn)類
//方法調(diào)用句柄invoke方法內(nèi)部就是代理類的擴(kuò)展點(diǎn)
public class DynamicProxy implements InvocationHandler{

    private Object target;//反射代理目標(biāo)類(被代理,解耦的目標(biāo)類)
    
    //可以通過構(gòu)造器動(dòng)態(tài)設(shè)置被代理目標(biāo)類,以便于調(diào)用指定方法
    public DynamicProxy(Object subject){
        this.target = subject;
    }
    
    //代理過程中的擴(kuò)展點(diǎn)
    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】客戶端調(diào)用:
在定義完動(dòng)態(tài)代理幾種基本類型之后,就可以在客戶端中進(jìn)行代理實(shí)現(xiàn)。為了更清楚說明這個(gè)動(dòng)態(tài)代理的過程,可以分為以下兩種情形來進(jìn)行使用分析:
(1)冗余型:過程清新,代碼冗余

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

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

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

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

  1. 最后方法的調(diào)用一定是聲明在接口的方法。只是具體動(dòng)態(tài)方法調(diào)用的時(shí)候,執(zhí)行的是接口實(shí)現(xiàn)子類中方法。
  2. 方法的調(diào)用點(diǎn)一定是在InvocationHandler接口或者其子類的invoke方法中,并且該接口中存在目標(biāo)類的對象(依賴),這里也是代理類的拓展點(diǎn)。
  3. 方法調(diào)用的對象,一定是通過構(gòu)造器形式來創(chuàng)建出來的。

在按照規(guī)定,正確使用了proxy動(dòng)態(tài)代理之后,會思考:為什么jdk的代理一定是基于接口實(shí)現(xiàn)的呢?
這里,可以先說說分析后的想法:

因?yàn)樵趧?dòng)態(tài)代理過程中,會生成對應(yīng)的代理類形如$Proxy0,$Proxy1....</font>這樣的匿名類,這些類都是繼承java.lang.reflect.Proxy類和實(shí)現(xiàn)了我們傳入
的接口,形如:public final class $Proxy0 extends Proxy implements Subject .所以,通過Proxy.newProxyInstance方法返回的就是這個(gè)匿名類的實(shí)例。通常就通過強(qiáng)制轉(zhuǎn)換成指定接口,最后就可以調(diào)用方法了。【java中不能多重繼承,當(dāng)代理匿名類實(shí)現(xiàn)了jdk中的Proxy.class類的時(shí)候,就只能通過實(shí)現(xiàn)目標(biāo)接口的方式來實(shí)現(xiàn)拓展

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

若是強(qiáng)制轉(zhuǎn)換成接口實(shí)現(xiàn)子類,形如:

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

就會報(bào)如下錯(cuò)誤:
java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to proxy.RealSubject。

代理流程分析

就基于上述使用的樣例中的動(dòng)態(tài)代理的代碼,對每個(gè)步驟的過程進(jìn)行分析??纯?,每個(gè)步驟內(nèi)部是如何運(yùn)行的。
因?yàn)?,整個(gè)jdk動(dòng)態(tài)代理過程都與java.lang.reflect.Proxy類有關(guān),就先看看該類中一些重要分屬性字段:


image

生成代理類

然后,整體看看創(chuàng)建代理對象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());
        }
    }

結(jié)合上述代碼,來講講幾個(gè)重要的階段:
【1】傳入目標(biāo)接口類和處理器InvocationHandler實(shí)現(xiàn)類
可以看到客戶端調(diào)用newProxyInstance方法,傳入了類加載器,接口類還有句柄處理器Invocationhandler實(shí)現(xiàn)類。傳入的這些類都是為下面步驟做基礎(chǔ)的。

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

【2】根據(jù)類加載器和目標(biāo)接口類獲取代理類Class對象:
這部分也是代理類的核心,因?yàn)檫@方法里面包含了代理類的動(dòng)態(tài)創(chuàng)建過程。會生成一個(gè)形如$Proxy0...之類的動(dòng)態(tài)代理類,然后JVM會加載這些字節(jié)類,得到對應(yīng)的Class對象,進(jìn)行緩存。下面看看過程:

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

根據(jù)源代碼來逐步分析分析:(最重要的就是生成代理類字節(jié)碼$Proxy0...$Proxyn,還有就是進(jìn)行緩存
(1)首先創(chuàng)建一個(gè)字符串?dāng)?shù)組,用于存放入?yún)nterfaces中的所有不同接口的類型。便于后面使用全限定類名字符串創(chuàng)建類實(shí)例和緩存處理。

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

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

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

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

因?yàn)榕袛嗍欠袷峭粋€(gè)類,需要與將類加載器與類一起判定。

(4)往下,就是通過這些類名,結(jié)合類加載器來創(chuàng)建或者查詢指定的Class代理類對象?,F(xiàn)將interfaceNames字符數(shù)組中類字符串轉(zhuǎn)化為List<String>,

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

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

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

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

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

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

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

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出入構(gòu)造器來查找構(gòu)造器實(shí)例的原理
  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);
    }
  }

//這個(gè)是我們要調(diào)用的目標(biāo)類中方法
  public final void hello(String paramString)
    throws 
  {
    try
    {
     //實(shí)際上可以看到,是調(diào)用了我們自定義的InvocationHandler接口實(shí)現(xiàn)類的invoke方法。
    // m3這個(gè)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);
    }
  }

//靜態(tài)代碼塊,用于通過反射來初始化四個(gè)方法屬性
  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】根據(jù)得到的代理類Class對象,通過反射獲取指定構(gòu)造器類對象,且并未創(chuàng)建對象:

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

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

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

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

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

  2. 返回復(fù)制好的新的構(gòu)造器對象。

【4】根據(jù)得到的構(gòu)造器類創(chuàng)建代理類實(shí)例:newInstance(cons,InvocationHandler)</font>
調(diào)用入?yún)?gòu)造器的newInstance方法,通過反射創(chuàng)建代理類的對象。

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

那么,構(gòu)造器創(chuàng)建實(shí)例內(nèi)部是如何運(yùn)行的:

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

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

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

代理類方法調(diào)用

當(dāng)通過上面的流程運(yùn)行過后,就能生成并返回一個(gè)動(dòng)態(tài)代理類<font color="red">$Proxy0</font>的實(shí)例對象。該代理類內(nèi)部的源代碼通過上述步驟中,也能看到了。剩下的,就是調(diào)用返回的目標(biāo)接口實(shí)現(xiàn)類:$Proxy0類的對象調(diào)用接口中聲明中方法,進(jìn)行功能增強(qiáng)了:

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

那么,這個(gè)調(diào)用的方法實(shí)際上如何運(yùn)行的呢?結(jié)合$Proxy0類的源代碼來說明:
【1】首先,在動(dòng)態(tài)代理類$Proxy0中的靜態(tài)代碼塊中,通過反射首先獲得被代理目標(biāo)類中的調(diào)用方法方法參數(shù)。因?yàn)樽罱K的方法調(diào)用,還是要通過該反射得到的Method對象進(jìn)行調(diào)用的。</font>

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

可以看到,是通過反射得到代理目標(biāo)類中的拓展點(diǎn)方法的Method對象。
【2】調(diào)用擴(kuò)展的方法,內(nèi)部實(shí)際是調(diào)用調(diào)用處理器InvocationHandler實(shí)現(xiàn)類的invoke方法。這就是為什么我們要自定義InvocationHandler實(shí)現(xiàn)類,并重寫該接口的invoke方法。并傳入代理目標(biāo)類this,被擴(kuò)展方法m3,方法參數(shù)等等。

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

可以看到,當(dāng)我們在調(diào)用代理返回的代理對象的hello方法的時(shí)候,其實(shí)底層調(diào)用的是我們自定義InvaotionHandler實(shí)現(xiàn)類的invoke方法。其中,變量this.h是那里來的呢?其實(shí),就是通過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內(nèi)部invoke方法調(diào)用:
我們可控的調(diào)用鏈就是到自定義InvocationHandler實(shí)現(xiàn)類的invoke方法就終止了,看看我們定義的實(shí)現(xiàn)類invoke實(shí)現(xiàn):

public class DynamicProxy implements InvocationHandler{

    private Object target;//反射代理目標(biāo)類(被代理,解耦的目標(biāo)類)
    
    //可以通過構(gòu)造器動(dòng)態(tài)設(shè)置被代理目標(biāo)類,以便于調(diào)用指定方法
    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方法內(nèi),最后就是調(diào)用的是m3.invoke方法。關(guān)于反射方法的invoke方法的內(nèi)部實(shí)現(xiàn),可以參考:反射代理類加載器的潛在內(nèi)存使用問題.
總結(jié)來說,就是通過Method.copy方法拷貝一個(gè)相同的方法,并調(diào)用所有方法共享的MethodAccessor對象來實(shí)際調(diào)用。

所以,Proxy代理類的執(zhí)行過程:

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

同時(shí)我們一定要記住,通過 Proxy.newProxyInstance 創(chuàng)建的代理對象是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)對象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運(yùn)行是動(dòng)態(tài)生成的一個(gè)對象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最后一個(gè)數(shù)字表示對象的標(biāo)號。
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 參數(shù)可以在項(xiàng)目根目錄下的com/sun/proxy/目錄下生成$Proxy0,1....代理類字節(jié)碼文件?!?/p>

另外說說代理有關(guān)的Inflation機(jī)制
若是想測試看看Inflation機(jī)制:
調(diào)用次數(shù)大于15或者設(shè)置noInflation=true,那么就可以看到GeneratedConstructorAccessorXXX和GeneratedMethodAccessorXXX類被JVM加載。
可以通過JVM參數(shù)-XX:+TraceClassLoading-Dsun.reflect.noInflation=true來查看JVM加載了這兩種類型的類。(ps: 只有接口實(shí)例調(diào)用方法才會有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]

若是按照常規(guī)noInflation=false的時(shí)候,就應(yīng)該調(diào)用JNI來通過本地Native實(shí)現(xiàn)類來生成和加載類:(且有緩存,通常只是加載一遍那個(gè)類)
[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]


完結(jié)。

image

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容