JDK动态代理源码学习,JDK动态代理原理

发布时间:2019-10-04  栏目:编程  评论:0 Comments

上回,简单的看了JDK动态代理与CGLIB动态代理的应用于区别,也了解了下Proxy.newProxyInstance()这个类参数与作用。这次我们接着往下看并提出几个疑问来引导下面的内容。

继上一篇博客设计模式之代理模式学习之后
Proxy类。该类即为动态代理类,可以使用反编译工具查看jdk里源码。JDK动态代理实现主要由Proxy类的newProxyInstance()方法实现。实现起来很容易,但是学习都要学习原理,所以本博客主要介绍jdk动态代理实现的源码。

在上一篇里为大家简单介绍了什么是代理模式?为什么要使用代理模式?并用例子演示了一下静态代理和动态代理的实现,分析了静态代理和动态代理各自的优缺点。在这一篇中笔者打算深入源码为大家剖析JDK动态代理实现的机制,建议读者阅读本篇前可先阅读一下笔者上一篇关于代理模式的介绍《JDK动态代理[1]—-代理模式实现方式的概要介绍》

  • 这个代理类到底哪来的?
  • invoke()方法到底做了什么,怎么和我们的方法建立联系的?
  • 生成的代理类是什么样的?

newProxyInstance()方法用于根据传入的接口类型interfaces返回一个动态创建的代理类的实例,方法中第一个参数loader表示代理类的类加载器,第二个参数interfaces表示被代理类实现的接口列表,第三个参数h表示所指派的调用处理程序类。

上一篇动态代理的测试类中使用了Proxy类的静态方法newProxyInstance方法去生成一个代理类,这个静态方法接收三个参数,分别是目标类的类加载器,目标类实现的接口集合,InvocationHandler实例,最后返回一个Object类型的代理类。我们先从该方法开始,看看代理类是怎样一步一步造出来的,废话不多说,直接上代码

再次探索Proxy.newProxyInstance

抽出主要的3条关键语句(可在IDEA中通过双击关键词来观察代码的走向)分别是:克隆-生成指定的代理类-得到构造器-返回代理类可以得知:生成代理类的内容在第二句话中,那么点进去~

final Class<?>[] intfs = interfaces.clone();Class<?> cl = getProxyClass0(loader, intfs);final Constructor<?> cons = cl.getConstructor(constructorParams);return cons.newInstance(new Object[]{h});

方法头注释我没加进来,意思是欲生成代理类,必先调用checkProxyAccess方法来执行权限检查,而在newProxyInstance方法的前几行遍执行了这个检查。下面的3行单行注释意为:这个东西是有缓存的,如果有就不要用ProxyClassFactory再生成。这边只有一个可以进入的方法,进入他!

private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces);}

这个方法大量是对缓存的操作,从高频的get(),replace(),和putIfAbsen(),我们都不管。进入一个参数正好为之前传递进来的key和parameter的apply方法。apply方法是BiFunction<T,
U, R>接口的一个定义方法,他是一个函数式接口,在《Java8 in
Action》P47也有他的身影。这种定义通常是表示为->R,意为通过2个类型产生一个类型。这个方法有2个实现,幸好我们上面再注释中看到了ProxyClassFactory,进入他!

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

前面就是通过对接口的遍历来验证接口,在方法的尾部我们看到一个byte[]字节数组,并从注释得知他的作用是生成指定的代理类。我们点进generateProxyClass方法,发现他有几个参数不同的重载方法。

/* * Generate the specified proxy class. */byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString;}

下面我们用generateProxyClass方法把返回的字节码写入到文件中来查看其中的东西。

public class GenerateProxy { public static void main(String[] args) { Talking talking = new JDKProxy(new TalkingImpl.getProxy(); talking.say; String path = "C:/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", TalkingImpl.class.getInterfaces; try (FileOutputStream out = new FileOutputStream{ out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } }}

因为IDEA自带反编译功能,直接把生成的$Proxy0.class文件拖入IDEA即可。

public final class $Proxy0 extends Proxy implements Talking { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super; } public final boolean equals(Object var1) throws { try { return super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException; } } public final String toString() throws { try { return super.h.invoke(this, m2, null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException; } } public final void say(String var1) throws { try { super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException; } } public final int hashCode() throws { try { return super.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException; } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.company.proxy.Talking").getMethod("say", Class.forName("java.lang.String")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage; } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage; } }}

可以看到代理类继承于Proxy并实现了Talking接口,并可以看到定义了4个方法与静态块通过反射用来初始化。到这里,一切都豁然开朗了。

接下来回答上面3个问题:

  • 是通过ProxyGenerator.generateProxyClass(String,
    Class<?>[])来生成的字节码
  • invoke方法即建立生成代理类与原被代理接口之间的联系
  • 生成的代理类就是酱紫的~

首先写个例子实现jdk动态代理
主题接口类:

newProxyInstance方法:

public interface IHello {
    public void sayHello();
}
 1 public static Object newProxyInstance(ClassLoader loader,
 2                                       Class<?>[] interfaces,
 3                                       InvocationHandler h) throws IllegalArgumentException {
 4     //验证传入的InvocationHandler不能为空
 5     Objects.requireNonNull(h);
 6     //复制代理类实现的所有接口
 7     final Class<?>[] intfs = interfaces.clone();
 8     //获取安全管理器
 9     final SecurityManager sm = System.getSecurityManager();
10     //进行一些权限检验
11     if (sm != null) {
12         checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
13     }
14     //该方法先从缓存获取代理类, 如果没有再去生成一个代理类
15     Class<?> cl = getProxyClass0(loader, intfs);
16     try {
17         //进行一些权限检验
18         if (sm != null) {
19             checkNewProxyPermission(Reflection.getCallerClass(), cl);
20         }
21         //获取参数类型是InvocationHandler.class的代理类构造器
22         final Constructor<?> cons = cl.getConstructor(constructorParams);
23         final InvocationHandler ih = h;
24         //如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问
25         if (!Modifier.isPublic(cl.getModifiers())) {
26             AccessController.doPrivileged(new PrivilegedAction<Void>() {
27                 public Void run() {
28                     cons.setAccessible(true);
29                     return null;
30                 }
31             });
32         }
33         //传入InvocationHandler实例去构造一个代理类的实例
34         //所有代理类都继承自Proxy, 因此这里会调用Proxy的构造器将InvocationHandler引用传入
35         return cons.newInstance(new Object[]{h});
36     } catch (Exception e) {
37         //为了节省篇幅, 笔者统一用Exception捕获了所有异常
38         throw new InternalError(e.toString(), e);
39     }
40 }

被代理的类:

可以看到,newProxyInstance方法首先是对参数进行一些权限校验,之后通过调用getProxyClass0方法生成了代理类的类对象,然后获取参数类型是InvocationHandler.class的代理类构造器。检验构造器是否可以访问,最后传入InvocationHandler实例的引用去构造出一个代理类实例,InvocationHandler实例的引用其实是Proxy持有着,因为生成的代理类默认继承自Proxy,所以最后会调用Proxy的构造器将引用传入。在这里我们重点关注getProxyClass0这个方法,看看代理类的Class对象是怎样来的,下面贴上该方法的代码

public class Hello implements IHello{

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

getProxyClass0方法:

JDK动态代理InvocationHandler类

1 private static Class<?> getProxyClass0(ClassLoader loader,
2                                        Class<?>... interfaces) {
3     //目标类实现的接口不能大于65535
4     if (interfaces.length > 65535) {
5         throw new IllegalArgumentException("interface limit exceeded");
6     }
7     //获取代理类使用了缓存机制
8     return proxyClassCache.get(loader, interfaces);
9 }
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {
    private Object target;//委托类
    public MyInvocationHandler(Object target){
        this.target=target;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        /**代理环绕**/
        //执行实际的方法
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

可以看到getProxyClass0方法内部没有多少内容,首先是检查目标代理类实现的接口不能大于65535这个数,之后是通过类加载器和接口集合去缓存里面获取,如果能找到代理类就直接返回,否则就会调用ProxyClassFactory这个工厂去生成一个代理类。关于这里使用到的缓存机制我们留到下一篇专门介绍,首先我们先看看这个工厂类是怎样生成代理类的。

写个类模拟字节码文件生成:

ProxyClassFactory工厂类:

留下评论

网站地图xml地图