【面试精讲】Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别?

Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别?

目录

一、Java动态代理的实现

1、使用JDK Proxy实现动态代理

2、使用CGLib实现动态代理

二、JDK Proxy 与 CGLib 的区别

三、Spring中的动态代理

四、 Lombok代理原理

总结


前言

本文深入探讨了Java动态代理的实现机制,分别介绍了使用JDK Proxy和CGLib两种不同方式来实现动态代理。

文章进一步对比了JDK Proxy与CGLib的主要区别,JDK Proxy主要依赖于java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口,它允许在运行时动态创建实现了一组接口的代理类实例,但仅限于代理那些已实现接口的类。相比之下,CGLib通过在运行时生成目标类的子类来实现代理,从而克服了JDK Proxy只能代理接口的限制。包括代理对象的类型、性能差异、使用场景以及依赖问题。尽管两者都可以实现动态代理,但选择哪一种方式取决于具体需求:如果目标对象已经实现了接口,则JDK Proxy是一个简单直接的选择;若需要代理没有实现接口的普通类,则CGLib是更合适的选择。

文中还探讨了Spring框架中动态代理的应用,Spring可以根据情况自动选择使用JDK Proxy或CGLib来实现代理,为开发者提供了更高层次的抽象和便利。同时,也简要介绍了Lombok库是如何利用代理原理来减少样板代码并提高开发效率的。

一、Java动态代理的实现

Java动态代理主要依赖于java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。其基本思想是,在运行时动态创建一个实现了一组接口的代理类的实例,然后将对该实例的所有方法调用转发给一个处理器(即实现了InvocationHandler接口的类的实例)。

Java动态代理是Java高级编程中的一项强大功能,它允许开发者在运行时创建代理实例来控制对其他对象的访问。这种技术广泛应用于AOP(面向切面编程)、RPC(远程过程调用)框架、事务管理等领域。本文旨在深入探讨Java动态代理的实现机制,并比较JDK Proxy和CGLib两种实现方式的异同。

动态代理的常用实现方式是反射。反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。

动态代理可以通过 CGLib 来实现,而 CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。

简单来说,动态代理是一种行为方式,而反射或 ASM 只是它的一种实现手段而已。

1、使用JDK Proxy实现动态代理

JDK动态代理只能代理实现了接口的类。

  • 定义接口:首先定义一个或多个接口,以及它们的实现类。
  • 创建InvocationHandler:实现InvocationHandler接口,重写invoke方法。在这里可以插入自定义的逻辑,比如日志记录、权限检查等。
  • 生成代理对象:通过调用Proxy.newProxyInstance方法,传入类加载器、接口数组以及InvocationHandler实例,来动态生成代理对象。
public interface Subject {void doSomething();
}public class RealSubject implements Subject {@Overridepublic void doSomething() {System.out.println("Doing something...");}
}public class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// Before adviceSystem.out.println("Before method call");Object result = method.invoke(target, args);// After adviceSystem.out.println("After method call");return result;}
}Subject subject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),new Class[]{Subject.class},new MyInvocationHandler(new RealSubject())
);subject.doSomething();

2、使用CGLib实现动态代理

与JDK Proxy不同,CGLib能够代理未实现接口的类。它通过在运行时动态生成被代理类的子类,来拦截对父类方法的调用。

  • 定义类:直接定义一个类,而非接口及其实现。
  • 创建MethodInterceptor:实现MethodInterceptor接口,重写intercept方法,在该方法中添加自定义逻辑。
  • 生成代理对象:通过使用CGLib提供的Enhancer类,设置父类和回调,从而创建代理对象。
public class RealSubject {public void doSomething() {System.out.println("Doing something...");}
}Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// Before adviceSystem.out.println("Before method call");Object result = proxy.invokeSuper(obj, args);// After adviceSystem.out.println("After method call");return result;}
});RealSubject subject = (RealSubject) enhancer.create();
subject.doSomething();

二、JDK Proxy 与 CGLib 的区别

尽管JDK Proxy和CGLib都可用于实现Java动态代理,但它们之间存在一些关键差异:

  1. 代理对象类型:JDK Proxy只能代理实现了接口的类;而CGLib可以直接代理普通类。
  2. 性能:CGLib在运行时生成代理类的子类,通常认为其性能略优于JDK Proxy。但在大多数场景下,这种性能差异不大。
  3. 使用场景:如果目标对象已经实现了接口,使用JDK Proxy是一个简单直接的选择。如果需要代理没有实现接口的类,则必须使用CGLib。
  4. 依赖:JDK Proxy无需额外依赖,因为它是Java核心库的一部分;而CGLib需要添加CGLib库作为项目依赖。
  5. JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;
  6. Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
  7. JDK Proxy 是通过拦截器加反射的方式实现的;
  8. JDK Proxy 只能代理继承接口的类;
  9. JDK Proxy 实现和调用起来比较简单;
  10. CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
  11. CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。

三、Spring中的动态代理

动态代理的常见使用场景有 RPC 框架的封装、AOP(面向切面编程)的实现、JDBC 的连接等。

Spring 框架中同时使用了两种动态代理 JDK Proxy 和 CGLib,当 Bean 实现了接口时,Spring 就会使用 JDK Proxy,在没有实现接口时就会使用 CGLib,我们也可以在配置中指定强制使用 CGLib,只需要在 Spring 配置中添加  <aop:aspectj-autoproxy proxy-target-class="true"/>  即可。

四、 Lombok代理原理

Lombok是一个工具类可以解决又重复的代码,如 Setter、Getter、toString、equals 和 hashCode 等等,向这种方法都可以使用 Lombok 注解来完成。 需要在 IDE 中安装 Lombok 插件,如下图所示:

Lombok 的实现和反射没有任何关系,Lombok 是在编译期就为我们生成了对应的字节码其实 Lombok 是基于 Java 1.6 实现的 JSR 269: Pluggable Annotation Processing API 来实现的,也就是通过编译期自定义注解处理器来实现的,它的执行步骤如下:

 从流程图中可以看出,在编译期阶段,当 Java 源码被抽象成语法树(AST)之后,Lombok 会根据自己的注解处理器动态修改 AST,增加新的代码(节点),在这一切执行之后就生成了最终的字节码(.class)文件,这就是 Lombok 的执行原理。

总结

Java动态代理是Java高级编程中的一个强大工具,它提供了一种灵活的方式来增强方法调用。通过对比JDK Proxy和CGLib,可以看出每种方法各有优势和适用场景。选择合适的动态代理实现方式,可以帮助开发者编写更加灵活和高效的代码。在实际开发中,根据具体需求选择最适合的代理方式,是提升项目质量和维护性的关键。

如果本文对你有帮助 欢迎 关注 、点赞 、收藏 、评论, 博主才有动力持续记录遇到的问题!!!

博主v:XiaoMing_Java

 📫作者简介:嗨,大家好,我是  小明(小明Java问道之路)互联网大厂后端研发专家,2022博客之星TOP3 / 博客专家 / CSDN后端内容合伙人、InfoQ(极客时间)签约作者、阿里云签约博主、全网 6 万粉丝博主。


🍅 文末获取联系 🍅  👇🏻 精彩专栏推荐订阅收藏 👇🏻

专栏系列(点击解锁)

学习路线(点击解锁)

知识定位

🔥Redis从入门到精通与实战🔥

Redis从入门到精通与实战

围绕原理源码讲解Redis面试知识点与实战

🔥MySQL从入门到精通🔥

MySQL从入门到精通

全面讲解MySQL知识与企业级MySQL实战

🔥计算机底层原理🔥

深入理解计算机系统CSAPP

以深入理解计算机系统为基石,构件计算机体系和计算机思维

Linux内核源码解析

围绕Linux内核讲解计算机底层原理与并发

🔥数据结构与企业题库精讲🔥

数据结构与企业题库精讲

结合工作经验深入浅出,适合各层次,笔试面试算法题精讲

🔥互联网架构分析与实战🔥

企业系统架构分析实践与落地

行业最前沿视角,专注于技术架构升级路线、架构实践

互联网企业防资损实践

互联网金融公司的防资损方法论、代码与实践

🔥Java全栈白宝书🔥

精通Java8与函数式编程

本专栏以实战为基础,逐步深入Java8以及未来的编程模式

深入理解JVM

详细介绍内存区域、字节码、方法底层,类加载和GC等知识

深入理解高并发编程

深入Liunx内核、汇编、C++全方位理解并发编程

Spring源码分析

Spring核心七IOC/AOP等源码分析

MyBatis源码分析

MyBatis核心源码分析

Java核心技术

只讲Java核心技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/731817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

21 easy 1. 两数之和

//给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 // // 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 // // 你可以…

Day18:信息打点-小程序应用解包反编译动态调试抓包静态分析源码架构

目录 小程序获取-各大平台&关键字搜索 小程序体验-凡科建站&模版测试上线 小程序抓包-Proxifier&BurpSuite联动 小程序逆向-解包反编译&动态调试&架构 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系…

设计模式-行为型模式-职责链模式

在软件系统运行时&#xff0c;对象并不是孤立存在的&#xff0c;它们可以通过相互通信协作完成某些功能&#xff0c;一个对象在运行时也将影响到其他对象的运行。行为型模式&#xff08;Behavioral Pattern&#xff09;关注系统中对象之间的交互&#xff0c;研究系统在运行时对…

C++:多态

目录 1、多态的概念 2、多态如何实现 虚函数 虚函数的重写 虚函数重写的两个例外&#xff1a; c11中 override与final 3、重载 、重写、重定义的区别。 1、重载 2、重写 3、重定义 接口继承和实现继承 4、动态绑定与静态绑定 5、虚函数表 1、单继承中虚函数表 …

Uber/Google Golang编码标准深度分析

良好的代码风格对于开发优秀的产品至关重要&#xff0c;本文通过分析比较三部流传甚广的Golang代码风格指南&#xff0c;介绍了Go代码风格要点&#xff0c;并介绍了通过工具实现代码检查的方式。原文: Mastering Go: In-Depth Analysis of Uber and Google’s Coding Standards…

C++矢量运算与java矢量运算

矢量运算 概述&#xff1a; 矢量运算是一种基于向量的数学运算&#xff0c;它遵循特定的法则。以下是矢量运算的一些基本原理&#xff1a; 矢量加法&#xff1a;可以使用平行四边形法则或三角形法则来执行。当两个矢量相加时&#xff0c;可以将它们的起点放在同一个点上&…

RabbitMQ篇

1.初始MQ 1.1. 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;…

初阶数据结构:排序(学习笔记)

目录 1. 各种排序算法的分类2. 插入排序2.1 直接插入排序2.2 希尔排序 3. 选择排序3.1 选择排序3.2 堆排序4. 交换排序4.1 冒泡排序4.2 快速排序4.2.1 霍尔法&#xff08;hoare&#xff09;4.2.2 挖坑法&#xff08;hole&#xff09;4.4.3 前后指针法4.4.4 补充&#xff1a;非递…

存货计价方式 比较-移动平均和批次计价

SAP常用的存货计价方式有 标准价格移动平均价格批次计价 标准价格常用于制造企业&#xff0c;今天的方案比较主要集中在销售型企业常用的移动平均价和批次计价 批次计价&#xff1a; 移动平均&#xff1a; 两种计价方式的Pros&Cons 比较 批次计价 移动平均优点 1…

超好用的一键生成原创文案方法

在现代社会中&#xff0c;原创文案不管是在营销中&#xff0c;还是在品牌推广中都起着至关重要的作用。然而&#xff0c;对于许多人来说&#xff0c;创作出令人印象深刻且引人注目的原创文案并不容易。但随着技术的发展&#xff0c;我们现在可以利用一键生成原创文案的方法来帮…

黑马java-JavaSE进阶-java高级技术

1.单元测试 就是针对最小的功能单元方法&#xff0c;编写测试代码对其进行正确性测试 2.Junit单元测试框架 可以用来对方法进行测试&#xff0c;它是第三方公司开源出来的 优点&#xff1a; 可以灵活的编写测试代码&#xff0c;可以针对某个方法执行测试&#xff0c;也支持一键…

基于springboot的水果购物商城管理系统(程序+文档+数据库)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

Pinctrl子系统_04_Pinctrl子系统主要数据结构

引言 本节说明Pinctrl子系统中主要的数据结构&#xff0c;对这些数据结构有所了解&#xff0c;也就是对Pinctrl子系统有所了解了。 前面说过&#xff0c;要使用Pinctrl子系统&#xff0c;就需要去配置设备树。 以内核面向对象的思想&#xff0c;设备树可以分为两部分&#x…

rabbitmq3

指定通过通道将消息发送给哪个消息队列 同一个通道可以向不同的队列发送消息的&#xff0c;如果你绑定的队列和发布消息的队列不一致也是可以的&#xff0c;这个才是真正的发布消息去具体的某一个队列&#xff1a; 如果队列没有持久化&#xff0c;就不会把这个消息队列保存在磁…

stm32学习记录-5.2PWM输出控制sg90舵机角度

源码连接&#xff1a;https://gitee.com/HL12334/stm32-learning-code 前提知识&#xff1a; 1.定时器中断 1.关键概念 1.1pwm输出 1.常用术语 OC&#xff08;output compare&#xff09;输出比较CNT&#xff08;counter&#xff09;&#xff0c;定时器中用于计数的寄存器…

时序报告Report_timing_summary之一步精通配置选项使用

目录 一、前言 二、配置选项概览图 三、配置选项 3.1 Options 3.1.1 report 3.1.2 path limits 3.1.3 path display 3.2 Advanced 3.2.1 report 3.2.3 miscellaneous 3.3 Timer Settings 3.4 共有部分 四、工程示例 4.1 工程设计代码 4.2 约束文件 4.3 Option…

Linux系统架构----nginx的访问控制

nginx的访问控制 一、nginx基于授权的访问控制概述 Nginx与Apache一样&#xff0c;可以实现基于用户权限的访问控制&#xff0c;当客户端想要访问相应的网站或者目录时&#xff0c;要求用户输入用户名和密码&#xff0c;才能正常访问配置步骤生成用户密码认证文件 &#xff1…

qt带后缀单位的QLineEdit

QLineEditUnit.h #pragma once #include <QLineEdit> #include <QPushButton>class QLineEditUnit : public QLineEdit {Q_OBJECT public:QLineEditUnit(QWidget* parent Q_NULLPTR);~QLineEditUnit();//获取编辑框单位QString UnitText()const;//设置编辑框单位…

STM32的启动流程分析 和 一些底层控制的原理

阅读引言&#xff1a; 阅读本文之后&#xff0c; 你将对单片机&#xff0c; 甚至是嵌入式系统&#xff0c; 或者是传统的PC机系统的启动流程有一个大致的了解&#xff0c; 本文更加偏向于单片机的启动流程分析。 目录 一、基础知识 1.STM32系列的微控制器&#xff08;mcu&…

自研cloud框架专题–web模块(三)

项目特点一:框架集成 1.引入核心依赖2.配置相关功能 二:功能介绍 1.swagger支持并提供swagger快速配置2.knife增强swagger支持3.全局请求参数校验(Validation)支持4.字段脱敏支持5.默认jackson序列化6.xss,cors支持7.访问日志支持8.全局异常处理,统一返回结果9.系统关键及常用信…