【深度思考】聊聊JDK动态代理原理

这篇具有很好参考价值的文章主要介绍了【深度思考】聊聊JDK动态代理原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 示例

首先,定义一个接口:

public interface Staff {
    void work();
}

然后,新增一个类并实现上面的接口:

public class Coder implements Staff {
    @Override
    public void work() {
        System.out.println("认真写bug……");
    }
}

假设现在有这么一个需求:在不改动以上类代码的前提下,对该方法增加一些前置操作或者后置操作。

接下来就来讲解下,如何使用JDK动态代理来实现这个需求。

首先,自定义一个调用处理器,实现java.lang.reflect.InvocationHandler接口并重写invoke方法:

public class AttendanceInvocationHandler implements InvocationHandler {
    private final Object target;

    public AttendanceInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("上班打卡……");

        Object invoke = method.invoke(target, args);

        System.out.println("下班打卡……");

        return invoke;
    }
}

重点看下Object invoke = method.invoke(target, args);,该行代码会执行真正的目标方法,在这前后,我们可以添加一些增强逻辑。

然后,新建个测试类,看下JDK动态代理如何使用:

public class JdkProxyTest {
    public static void main(String[] args) {
        Coder coder = new Coder();
        AttendanceInvocationHandler h = new AttendanceInvocationHandler(coder);
        // 创建代理对象
        Object proxyInstance = Proxy.newProxyInstance(coder.getClass().getClassLoader(),
                coder.getClass().getInterfaces(),
                h);
        Staff staff = (Staff) proxyInstance;
        staff.work();
    }
}

运行以上代码,效果如下图所示:

从运行结果可以看出,在目标方法的前后,执行了自定义的操作。

2. 原理

这里理解2个概念,目标对象和代理对象,

目标对象是真正要调用的对象,上面示例中的Coder类就是目标对象,

代理对象是JDK自动生成的对象,在代理对象内部会去调用目标对象的目标方法。

JDK动态代理的核心就是上面示例中的Proxy.newProxyInstance方法,方法签名如下图所示:

第1个参数传入的是目标对象的ClassLoader,第2个参数传入的是目标对象的接口信息,第3个参数传入的是自定义的InvocationHandler。

然后看下该方法的实现逻辑,先看第1处重点:

注释翻译过来是:查找或者生成指定的代理类。

该方法会生成代理类的字节码文件(也可能是从缓存中读取),核心逻辑在ProxyClassFactory类的apply方法中,

该方法中定义了生成的代理类的包名以及文件名:

因此默认情况下,自动生成的代理类名称是com.sun.proxy.$Proxy0

该方法最后会生成代理类的字节码,默认情况下不会保存到文件系统,但可以通过参数指定保存到文件系统:

可以看出,保存不保存到文件系统,受saveGeneratedFiles的影响,其定义如下所示:

private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

所以可以通过指定sun.misc.ProxyGenerator.saveGeneratedFiles参数来让生成的代理类字节码文件保存到文件系统中。

然后看第2处重点:

先是获取构造函数,然后是生成代理类对象的实例。

3. 为什么必须要基于接口?

思考一个问题,为什么JDK动态代理必须要基于接口,带着这个问题,我们看下动态生成的代理类com.sun.proxy.$Proxy0长什么样子?

JVM参数里添加参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,然后启动上面示例中的测试代码:

生成的代理类字节码文件保存在项目根目录下的com/sun/proxy目录下:

在IDEA中打开后,如下图所示:

在静态代码块中,对静态变量m0、m1、m2、m3进行了赋值,其中m3是要执行的目标方法。

在构造方法中,执行的是super(var1);,也就是父类Proxy的构造方法:

该方法是将我们自定义的InvocationHandler赋值给了父类的变量h。

而以下测试代码实际执行的是代理类$Proxy0里的work方法:

Staff staff = (Staff) proxyInstance;
staff.work();

代理类$Proxy0里的work方法实际执行的是自定义InvocationHandler里的invoke方法:

因此在执行目标方法前后,执行了自定义的前置操作和后置操作。

了解了这个调用过程,就理解了为什么JDK动态代理必须要基于接口,因为动态生成的代理类已经继承了类java.lang.reflect.Proxy

而Java又是单继承的,如果想要继续对类进行扩展,只能通过实现接口的方式。

文章持续更新,欢迎关注微信公众号「申城异乡人」第一时间阅读!文章来源地址https://www.toymoban.com/news/detail-416171.html

到了这里,关于【深度思考】聊聊JDK动态代理原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【动态代理】JDK动态代理与cglib动态代理源码解析

    UserService ,接口类 UserServiceImpl ,实现类 使用代理,测试效果。 控制台输出: Proxy#newProxyInstance ,生成代理类的实例。 核心在于 getProxyClass0 ,生成代理类的类信息 使用自定义的InvocationHandler作为参数,调用构造函数获取代理类对象实例 WeakCache#get ProxyClassFactory#apply ,实现了

    2024年02月04日
    浏览(29)
  • JDK动态代理和CGLIB动态代理

    JDK动态代理和CGLIB动态代理 ① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的

    2024年02月07日
    浏览(29)
  • cglib动态代理、jdk动态代理及spring动态代理使用

    NickelBeforeAdvice.java方法执行之前 NickelAfterReturningAdvice.java方法执行之后 NickelAroundadvice.java环绕方法 NickelThrowAdvice.java抛异常方法 UserService.java抛异常方法 SpringProxtFactoryTest.java测试方法 NickelStaticMethodMatherPointcut.java方法匹配的方法 NickelPointcutAdvisor.java切面方法 SpringProxtFactoryTest.j

    2024年02月15日
    浏览(34)
  • 温故知新之:代理模式,静态代理和动态代理(JDK动态代理)

    代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。 静态代理 是一种代理模式的实现方式,它在编译期间就已经确定了代理对象,需要为每一个被代理对象创建一个代理类。静态代理的实现比较简单,但是每个被代理对象都需要创建

    2024年02月11日
    浏览(33)
  • java中的静态代理、jdk动态代理以及CGLIB 动态代理

    代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能 那以下文章主要谈三种代理模式, 分别是静态代理,jdk的动态代理,cglib的动

    2024年02月11日
    浏览(31)
  • 代理模式:静态代理+JDK/CGLIB 动态代理

    代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。 代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法

    2024年02月13日
    浏览(23)
  • 通俗易懂 快速理解 JDK动态代理 和 cglib动态代理

    动态代理的实现方案有两种, JDK动态代理 和 CGLIB动态代理 ,区别在于JDK自带的动态代理,必须要有接口,而CGLIB动态代理有没有接口都可以。 JDK动态代理 :JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求 代理对象和目标对象实现同样的接口 (兄

    2024年02月08日
    浏览(31)
  • 【spring】jdk动态代理和cglib动态代理的区别

    一、说明 1.spring aop中的动态代理主要有两种方式,jdk动态代理和cglib动态代理 2.从实现接口、继承父类的角度讨论区别 3.从限制角度讨论区别 4.从性能上讨论区别 二、区别 1.jdk动态代理只提供接口类的代理,如果目标类不是接口,只能用cglib代理 2.jdk动态代理会在运行时为目

    2024年02月16日
    浏览(29)
  • Java——JDK动态代理

    动态代理(理解) 基于反射机制 举个例子,生活中一般在打官司的时候都会请代理律师,为什么要请律师呢?是因为开庭的时候大部人对于打官司没有经验,只会说出自己案件的陈述,并不会根据法律等争取自己权益的最大化,此时就可以请律师帮助自己不仅完成对案件的陈述

    2024年02月09日
    浏览(21)
  • Java代理之jdk动态代理+应用场景实战

    本文将先介绍jdk动态代理的基本用法,并对其原理和注意事项予以说明。之后将以两个最常见的应用场景为例,进行代码实操。这两个应用场景分别是 拦截器 和 声明性接口 ,它们在许多开发框架中广泛使用。比如在spring和mybatis中均使用了拦截器模式,在mybatis中还利用动态

    2023年04月10日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包