分享

Jdk动态代理原理解析

 520jefferson 2021-09-24

作者:行径行

动态代理这个知识点,也是我们开发过程中非常容易遇到。特别的是在一些框架中,为了满足软件开发的开闭原则,以及增强框架自身的灵活拓展功能。在底层就会为那些特定的目标类或者接口实现类进行渲染与自定义功能操作。就如spring框架中的aop,底层也是通过动态代理来实现的,所以,就像看看jdk自身的动态代理是如
何实现的,整个过程是如何流动的。

图片

什么是动态代理,有什么优势?

动态,指的是代理类实在程序运行时创建的,而不是在程序运行前手动编码来定义代理类的。这些动态代理类是在运行时候根据我们在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的代理一定是基于接口实现的呢?
这里,可以先说说分析后的想法:

因为在动态代理过程中,会生成对应的代理类形如图片Proxy1….这样的匿名类,这些类都是继承java.lang.reflect.Proxy类和实现了我们传入
的接口,形如:public final 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.Proxy0extendsProxyimplementsSubject.所以,通过Proxy.newProxyInstance方法返回的就是这个匿名类的实例。通常就通过强制转换成指定接口,最后就可以调用方法了。【∗∗java中不能多重继承,当代理匿名类实现了jdk中的Proxy.class类的时候,就只能通过实现目标接口的方式来实现拓展∗∗】¨G3G若是强制转换成接口实现子类,形如:¨G4G就会报如下错误:java.lang.ClassCastException:com.sun.proxy.Proxy0 cannot be cast to proxy.RealSubject。

代理流程分析

就基于上述使用的样例中的动态代理的代码,对每个步骤的过程进行分析。看看,每个步骤内部是如何运行的。
因为,整个jdk动态代理过程都与java.lang.reflect.Proxy类有关,就先看看该类中一些重要分属性字段:

图片
生成代理类

然后,整体看看创建代理对象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对象:
这部分也是代理类的核心,因为这方法里面包含了代理类的动态创建过程。会生成一个形如

    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...之类的动态代理类,然后JVM会加载这些字节类,得到对应的Class对象,进行缓存。下面看看过程:¨G7G根据源代码来逐步分析分析:(最重要的就是生成代理类字节码∗∗Proxy0…

不能识别此Latex公式:
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,


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

然后创建一个 Map<list, Object>类型的cache变量,用于存放已经校验后加载过的类字符串,形如:</list


[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.最后,将生成的匿名代理字节类等信息保存到缓冲中: 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,1...∗∗等信息保存到缓冲中:cache.put(key,newWeakReference<Class<?>>(proxyClass)),创建了一个弱引用的代理类。5.4.返回该代理类对应的Class对象。那么生成的动态代理类是什么样呢?可以在代码中设置:¨C7C,会在项目根目录生成class字节文件,通过反编译可以看到:¨G11G【3】根据得到的代理类Class对象,通过反射获取指定构造器类对象,且并未创建对象:¨G12G∗∗为什么要传入一个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(Constructorargs)方法。
    E. 最后还是要调用Constructor.class中的copy()方法,来对找到的构造器进行复制。完成属性root和constructorAccessor属性的赋值。

  2. 返回复制好的新的构造器对象。

【4】根据得到的构造器类创建代理类实例:newInstance(cons,InvocationHandler)
调用入参构造器的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>的实例对象。该代理类内部的源代码通过上述步骤中,也能看到了。剩下的,就是调用返回的目标接口实现类:Proxy0∗∗</font>的实例对象。该代理类内部的源代码通过上述步骤中,也能看到了。剩下的,就是调用返回的目标接口实现类:Proxy0类的对象调用接口中声明中方法,进行功能增强了:

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

那么,这个调用的方法实际上如何运行的呢?结合图片Proxy0中的静态代码块中,通过反射首先获得被代理目标类中的调用方法方法参数。因为最终的方法调用,还是要通过该反射得到的Method对象进行调用的。

     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类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以图片Proxy0,1….代理类字节码文件。】

另外说说代理有关的Inflation机制
若是想测试看看Inflation机制:
调用次数大于15或者设置noInflation=true,那么就可以看到GeneratedConstructorAccessorXXX和GeneratedMethodAccessorXXX类被JVM加载。
可以通过JVM参数-XX:+TraceClassLoading-Dsun.reflect.noInflation=true来查看JVM加载了这两种类型的类。(ps: 只有接口实例调用方法才会有GeneratedMethodAccessorXXX)

[Loaded **sun.reflect.GeneratedConstructorAccessor1 from *JVM_DefineClass][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 *sun.reflect.GeneratedMethodAccessor1 from **JVM_DefineClass]
[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]

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多