Java静态代理和动态代理

通过一个小案例整理描述静态代理和动态代理

给大家举个简单例子。在一个公司中,老板处于上层,客户在下层。因每天来访客户众多,老板本应只考虑战略和赚钱,却被一些不重要的客户耽误不少时间。于是老板招聘了一个秘书,专门负责接待客户和登记客户来访。这就如同我们的代码,业务类只需负责核心业务代码,非业务代码如请求用时记录、事务开启、健全及异常处理等,交给代理去做,从而降低代码耦合度,将业务代码和公共代码分离,提高可维护性。这个例子属于静态代理,因为招来的秘书只负责客户来访登记。但还有很多事务需要安排,随着公司发展,王总、张总等也需要秘书,此时秘书类会增多,还需临时去找秘书。所以静态代理有局限性,在编译期就确定了要做的事情,无法代理全部方法,每个不同类型的目标类都需要一个代理类。

如何解决这种困境呢?通过动态代理可以解决。我们将人力公司视为动态代理,它可以动态派遣秘书,根据需求动态获取秘书。主流的人力公司有 JDK 和 CGLIB。它们的区别在于 JDK 需要提前签署协议,即 JDK 动态代理需要目标类实现接口,因为它生成的 class 是通过实现目标类的接口;而 CGLIB 则没有任何限制,因为生成的 class 是通过继承目标类。

那么 JDK 动态代理和 CGLIB 哪个更快呢?由于 JDK 动态代理底层是通过反射调用,没有 “合同” 得临时找,而 CGLIB 是实时调用,可以立马把 “合同” 派遣过来。听起来好像 JDK 动态代理更慢,但由于 JDK1.8 之后对反射进行了性能优化,所以在调用过程中性能不会有差别。

因此,我们可以根据目标类是否实现了接口,选择不同的动态代理技术。如果我们使用了 Spring AOP 的话,它的底层会根据我们使用的目标类,动态选择不同的动态代理技术。


一、静态代理

静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class 文件就已经存在了。

实现方式
- 定义一个接口,被代理对象和代理对象都实现这个接口。
- 代理对象持有被代理对象的引用,并在实现接口方法时,可以在调用被代理对象的方法前后添加额外的逻辑。
优点
- 结构简单直观,容易理解和实现。
- 可以在不修改被代理对象的情况下,为其添加额外的功能。
缺点
- 如果接口有很多方法,代理类需要实现所有方法,代码冗余。
- 当需要代理多个不同的对象时,需要为每个对象都创建一个代理类,不够灵活。

下面图为动态代理示例图

二、动态代理(JDK 动态代理)

JDK 动态代理是基于 Java 反射机制实现的。它要求被代理的对象必须实现一个接口。

实现方式
- 通过实现`InvocationHandler`接口创建一个调用处理器对象。
- 使用`Proxy.newProxyInstance()`方法创建代理对象。这个方法接收三个参数:类加载器、被代理对象实现的接口数组和调用处理器对象。
- 当调用代理对象的方法时,实际上会调用调用处理器的`invoke()`方法,在这个方法中可以添加额外的逻辑,并通过反射调用被代理对象的方法。
优点
- 代理对象的生成是在程序运行时动态完成的,更加灵活。
- 可以减少代码冗余,因为不需要为每个被代理对象都编写一个代理类。
缺点
- 只能代理实现了接口的对象。
三、CGLIB 动态代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库。它可以在运行时动态生成被代理对象的子类来实现代理功能。

实现方式
- 实现`MethodInterceptor`接口创建一个方法拦截器对象。
- 使用`Enhancer`类设置被代理对象的类、方法拦截器等信息,然后调用`create()`方法创建代理对象。
- 当调用代理对象的方法时,会调用方法拦截器的`intercept()`方法,在这个方法中可以添加额外的逻辑,并通过反射调用被代理对象的方法。
优点
- 可以代理没有实现接口的类。
- 性能较高,生成的代理类是被代理对象的子类,直接调用方法,不需要通过反射。
缺点
- 不能代理 final 类和 final 方法。
四、Spring Boot 中的代理方式

Spring Boot 主要使用了 JDK 动态代理和 CGLIB 动态代理。

  1. 当被代理的对象实现了一个或多个接口时,Spring 默认使用 JDK 动态代理。例如,当使用 Spring 的事务管理注解@Transactional时,如果被注解的对象实现了接口,Spring 会使用 JDK 动态代理来创建事务代理对象。
  2. 如果被代理的对象没有实现接口,Spring 会使用 CGLIB 来生成代理对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

救救孩子把

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

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

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

打赏作者

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

抵扣说明:

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

余额充值