jdk 动态代理实现原理

是什么?

动态代理就是动态地创建代理对象,即是在程序运行时刻生成的, 实现对目标对象的增强,动态代理是相对于静态代理而言的。

本质

动态代理的本质就是反射,用户提供类名,方法名,参数,可以根据传入不同的参数,生成不同的代理类,然后代理类执行方法,返回结果。

原理详解

在上一节中我们讲到了jdk 动态代理如何使用,但是仍然有以下几个疑问:

1.动态代理如何创建的呢?
2.当调用代理对象的addUser方法的时候,为什么会去执行invoke方法呢?

我们通过Proxy.newProxyInstance方法来创建代理对象

User userProxy=(User) Proxy.newProxyInstance(user.getClass().getClassLoader(),user.getClass().getInterfaces(),new MyInvocationHandler(user));

重点看Proxy的newPRoxyInstance方法:
这个方法有三个参数:

  • loader:类加载器,一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
  • interfaces:对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
  • h:一个InvocationHandler对象
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {   
        ...
        // 获得与制定类装载器和一组接口相关的代理类类型对象
        Class<?> cl = getProxyClass0(loader, intfs);
        // 通过反射获取构造函数对象并生成代理类实例
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        return cons.newInstance(new Object[]{h});
        ...
    }

在getProxyClass0方法中

     private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
       // 限定代理的接口不能超过65535个                                     
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

    // 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类
        return proxyClassCache.get(loader, interfaces);
    }

其中缓存使用的是WeakCache实现的,此处主要关注使用ProxyClassFactory创建代理的情况。ProxyClassFactory是Proxy类的静态内部类,实现了BiFunction接口,实现了BiFunction接口中的apply方法。当WeakCache中没有缓存相应接口的代理类,则会调用ProxyClassFactory类的apply方法来创建代理类。

    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {


            /*
             * 真正生成代理类的字节码文件的地方
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
               // 使用类加载器将代理类的字节码文件加载到JVM中
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }

generateProxyClass方法生成代理类对象

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        // 是否要将生成代理类的字节码文件保存到磁盘中
        if (saveGeneratedFiles) {
            // ....
        }
        return var4;
    }

InvocationHandler 的核心方法,我们最关心的是Invoke方法为什么会被调用,见下面分析:

// 该方法负责集中处理动态代理类上的所 有方法调用。
//第一个参数既是代理类实例,
//第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
 
Object invoke(Object proxy, Method method, Object[] args)

在NewProxyInstance方法生成的$Proxy0代理类的源码中,可看到

1、代理类继承了Proxy类并且实现了要代理的接口,由于java不支持多继承,所以JDK动态代理不能代理类

2、重写了equals、hashCode、toString

3、有一个静态代码块,通过反射或者代理类的所有方法

4、通过invoke执行代理类中的目标方法addUser

public final class $Proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;
 
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[] { Class.forName("java.lang.Object") });
 
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                    new Class[0]);
 
            m3 = Class.forName("***.RealSubject").getMethod("request",
                    new Class[0]);
 
            m2 = Class.forName("java.lang.Object").getMethod("toString",
                    new Class[0]);
 
        } catch (NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        } catch (ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    } //static
 
    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }
 
    @Override
    public final boolean equals(Object obj) {
        try {
            return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
    @Override
    public final int hashCode() {
        try {
            return ((Integer) super.h.invoke(this, m0, null)).intValue();
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
    public final void addUser() {
        try {
            super.h.invoke(this, m3, null); //就是这个地方  调用h.invoke()
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
 
    @Override
    public final String toString() {
        try {
            return (String) super.h.invoke(this, m2, null);
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}

从文件中可以看到addUser方法,可以看到中间调用了父类Proxy的h的invoke方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诗琪小姐姐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值