Spring代理方式之静态、动态代理(JDK和CGlib动态代理)

目录

1、代理设计模式的概念

2、静态代理

3、动态代理(JDK和CGlib动态代理)

1. JDK动态代理是基于接口的代理(Interface-based proxy)

2. CGLIB代理是基于类的代理(Class-based proxy)

⭐比较:JDK动态代理和CGLIB代理的区别

4、代理设计模式的目的和作用

小结


1、代理设计模式的概念

在Spring框架中,代理设计模式主要是将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用的目标。Spring框架利用代理模式来实现AOP(Aspect-Oriented Programming),通过代理对象对目标对象的方法调用进行拦截和增强,以实现横切关注点的功能。为此,spring中的代理方式可以分为静态代理和动态代理,而动态代理中又包含基于接口的JDK动态代理和基于类的CGLIB代理

P.S. 关于AOP的相关知识点本文不做详细介绍,后面会单独写一篇介绍。

图片来源:《Spring》

2、静态代理

在Spring中,静态代理是一种代理设计模式的实现方式。它通过手动编写代理类来对目标对象进行代理,并在代理类中调用目标对象的方法。

具体来说,静态代理需要满足以下条件:

  1. 定义一个接口(或者父类),该接口包含了目标对象和代理对象共同的方法。
  2. 创建一个代理类,实现上述接口,并持有一个目标对象的引用。
  3. 在代理类的方法中,可以在目标对象的方法调用前后加入额外的处理逻辑,比如日志记录、权限控制等。
  4. 在使用时,通过创建代理对象来替代直接使用目标对象,从而实现对目标对象方法的代理。

相对于动态代理,静态代理的主要特点是代理类在编译期间就已经存在,代理类在创建时,接口以及代理类就已经确定,因此称为静态代理。静态代理的优点是简单、直观,易于理解和实现。但是缺点也比较明显,每个目标对象都需要对应一个代理类,如果目标对象的方法发生变化,代理类也需要相应地做出修改。

值得注意的是,在Spring框架中,通常更多地使用动态代理来实现AOP的功能,因为动态代理可以更灵活地生成代理对象,避免了频繁编写和维护代理类的问题。但是了解静态代理的工作原理仍然是有益的,可以更好地理解AOP的实现原理。

通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利 于维护。

图片来源:《Spring》

代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法。

静态代理的问题:代理类数量过多,不利于项目的管理。 多个代理类的辅助功能代码冗余,修改时,维护性差。

3、动态代理(JDK和CGlib动态代理)

1. JDK动态代理是基于接口的代理(Interface-based proxy)

它要求被代理的目标对象必须实现一个或多个接口。JDK动态代理通过反射机制在运行时创建一个代理对象,代理对象实现了目标对象相同的接口,并且可以通过代理对象调用目标对象的方法。基于接口的代理要求目标对象必须实现接口,只能代理接口中定义的方法。

JDK动态代理的优点是代理对象的创建和使用都比较简单,缺点是只能代理实现了接口的目标对象。

下面是代码示例:

首先,定义一个接口,并创建实现类

public interface IUserService {void addUser(String username, String password);
}
public class UserService implements IUserService {@Overridepublic void addUser(String username, String password) {System.out.println("Add user: " + username + ", " + password);}
}

然后,使用JDK动态代理来生成代理对象。JDK动态代理需要目标对象实现一个接口,它利用Java反射机制在运行时动态地生成代理类。下面是JDK动态代理的实现代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkDynamicProxyExample {public static void main(String[] args) {IUserService target = new UserService(); // 创建目标对象// 创建JDK动态代理IUserService proxy = (IUserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), // 目标对象的类加载器target.getClass().getInterfaces(),  // 目标对象实现的接口new InvocationHandler() {           // 代理对象的调用处理程序@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());Object result = method.invoke(target, args); // 调用目标对象的方法System.out.println("After method: " + method.getName());return result;}});// 调用代理对象的方法proxy.addUser("Alice", "123456");}
}

运行上述代码,输出结果为:

Before method: addUser
Add user: Alice, 123456
After method: addUser

可以看到,在JDK动态代理中,通过InvocationHandler来实现代理对象的调用处理程序。在调用代理对象的方法时,实际上是通过反射机制调用目标对象的方法,并在方法调用前后加入了额外的处理逻辑。

2. CGLIB代理是基于类的代理(Class-based proxy)

它可以代理没有实现接口的目标对象。CGLIB(Code Generation Library)是一个强大的第三方类库,其代理通过继承目标对象并重写其中的方法来实现代理,因此它的代理对象是目标对象的子类。当目标对象没有实现任何接口时,Spring会使用CGLIB来创建代理对象。CGLIB会生成目标对象的子类,并拦截目标对象的方法调用。基于类的代理可以代理目标对象的所有方法,包括非公开方法和静态方法。

CGLIB代理的优点是可以代理没有实现接口的目标对象,缺点是代理对象的创建和使用比较复杂。

接下来,使用CGlib动态代理来生成代理对象。CGlib动态代理不需要目标对象实现一个接口,它利用ASM框架在运行时动态地生成代理类。下面是CGlib动态代理的实现代码:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class CGlibDynamicProxyExample {public static void main(String[] args) {UserService target = new UserService(); // 创建目标对象// 创建CGlib动态代理Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = proxy.invokeSuper(obj, args); // 调用目标对象的方法System.out.println("After method: " + method.getName());return result;}});UserService proxy = (UserService) enhancer.create();// 调用代理对象的方法proxy.addUser("Bob", "654321");}
}

运行上述代码,输出结果为:

Before method: addUser
Add user: Bob, 654321
After method: addUser

可以看到,在CGlib动态代理中,通过MethodInterceptor来实现代理对象的调用处理程序。在调用代理对象的方法时,实际上是通过MethodProxy调用目标对象的方法,并在方法调用前后加入了额外的处理逻辑。

Spring框架在选择代理方式时遵循以下规则:

  • 如果目标对象实现了至少一个接口,则默认使用基于接口的代理(jdk)。
  • 如果目标对象没有实现任何接口,则使用基于类的代理(cglib)。

可以通过配置Spring的AOP相关选项来控制代理方式。例如,可以使用<aop:config>元素来声明切面和通知,并通过proxy-target-class属性来指定是否使用基于类的代理(默认为false)。

需要注意的是,基于接口的代理要求目标对象实现接口,而基于类的代理则可以代理任何类型的对象。但是,由于基于类的代理需要生成子类,因此在创建代理对象时可能会引入一些性能开销。

⭐比较:JDK动态代理和CGLIB代理的区别

1.实现方式不同:JDK动态代理是基于接口实现的,它要求被代理类必须实现一个接口,代理类实现了这个接口并且调用被代理类的方法;而CGLIB动态代理是基于继承实现的,它可以代理没有实现接口的类,它通过生成被代理类的子类来实现代理。

2.性能不同:JDK动态代理是通过反射来调用被代理类的方法,因此它的性能比CGLIB动态代理要差一些;而CGLIB动态代理是通过生成字节码来调用被代理类的方法,因此它的性能比JDK动态代理要好一些。

3.适用场景不同:JDK动态代理适用于代理接口的情况,它可以为多个接口创建代理对象,而CGLIB动态代理适用于代理类的情况,它可以为单个类创建代理对象。

4、代理设计模式的目的和作用

代理设计模式的主要目的是在不改变原有类结构的情况下,为类的功能增加一些额外的处理逻辑。在Spring中,代理设计模式的作用主要包括以下几点:

  1. 实现横切关注点(Cross-Cutting Concerns): 通过代理模式,可以将与核心业务逻辑无关的功能,如日志记录、事务管理、安全性控制等,抽取出来形成独立的横切关注点,并在需要的时候将其应用到目标对象的方法调用中。这样可以使得关注点的代码得以重用,并且更容易实现集中管理。

  2. 解耦关注点和核心业务逻辑: 代理模式可以帮助将横切关注点与核心业务逻辑进行解耦。通过代理对象对方法调用的拦截和增强,可以将横切关注点的实现与核心业务逻辑分离开来,使得各部分之间的耦合度降低,提高了代码的可维护性和可扩展性。

  3. 实现通用性和复用性: 代理模式可以使得一些通用的横切关注点的实现得以复用,而不需要在每个类中都编写重复的代码。通过定义通用的拦截器或增强逻辑,在需要的时候将其应用到多个类的方法调用中,从而实现代码的通用性和复用性。

  4. 实现动态代理: 代理模式还可以实现动态代理,即在运行时动态地创建代理对象,根据需要在目标对象的方法调用前后插入额外的处理逻辑。这种动态代理的方式使得可以更加灵活地控制代理对象的行为,同时也为AOP的实现提供了基础。

小结

总之,Spring框架中的代理设计模式旨在通过代理对象对目标对象的方法调用进行拦截和增强,以实现横切关注点的功能,同时提高代码的可维护性和可扩展性。Spring动态代理提供了JDK动态代理和CGlib动态代理两种实现方式。它们都可以利用AOP技术来实现横切关注点的功能,但是具体的实现方式有所差异。在使用时需要根据实际情况选择合适的代理方式。

参考:

JDK动态代理和CGLIB动态代理_梵晞的博客-CSDN博客

Spring代理模式 - 風栖祈鸢 - 博客园

JAVA高级基础:Spring中AOP的两种代理方式动态代理和CGLIB详解_aop代理的两种方式-CSDN博客

Spring(2)——代理和AOP - 知乎

详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理 - 编程语言 - 亿速云

Spring_代理模式 - 只会干饭的杜某 - 博客园


感谢阅读,码字不易,多谢点赞!如有不当之处,欢迎反馈指出,感谢!

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

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

相关文章

SpringBoot : ch11 整合RabbitMQ

前言 在当今的互联网时代&#xff0c;消息队列成为了构建高可靠、高性能系统的重要组件之一。RabbitMQ作为一个可靠、灵活的消息中间件&#xff0c;被广泛应用于各种分布式系统中。 本篇博客将介绍如何使用Spring Boot整合RabbitMQ&#xff0c;实现消息的发送和接收。通过这种…

真空工艺腔内潮湿有什么危害?

在半导体制程中&#xff0c;真空工艺腔被广泛使用。薄膜沉积&#xff0c;干法刻蚀&#xff0c;光刻&#xff0c;退火&#xff0c;离子注入等工序均需要在相应的真空腔室中完成相应制程。真空工艺腔在半导体制程中起着至关重要的作用&#xff0c;它能够提供一个高度控制的环境&a…

MT8390(Genio 700)安卓核心板_MTK联发科工业AI主板Linux开发板

MT8390 (Genio 700) 安卓核心板是一款高性能边缘人工智能物联网平台&#xff0c;尺寸仅为45452.2mm。该平台提供高度响应的边缘处理、先进的多媒体功能、各种传感器和连接选项&#xff0c;同时支持多任务操作系统。 Genio 700处理器拥有PS APU性能&#xff0c;高效的芯片内人工…

42. 接雨水(单调栈)

这道题我本来的做法是 使用两个指针定住两头第一个非零元素&#xff0c;然后计算中间有多少个0&#xff0c;就表示多少个“坑” 然后依次-1&#xff0c;再重复上述操作 但是遇到height[i]为上千的数字&#xff0c;就很蠢了。。。 并且超时 class Solution { public:int trap(v…

同旺科技 USB 转 RS-485 适配器

内附链接 1、USB 转 RS-485 适配器 基础版主要特性有&#xff1a;&#xff08;非隔离&#xff09; ● 支持USB 2.0/3.0接口&#xff0c;并兼容USB 1.1接口&#xff1b; ● 支持USB总线供电&#xff1b; ● 支持Windows系统驱动&#xff0c;包含WIN10 / WIN11系统32 / 64位…

SSL证书实惠品牌——JoySSL

随着互联网的普及和发展&#xff0c;网络安全问题日益严重。为了保护网站数据的安全&#xff0c;越来越多的网站开始使用SSL证书。JoySSL证书作为一款高性价比的SSL证书&#xff0c;受到了广泛的关注和好评。 目前市面上主流的证书基本上都是国外证书&#xff0c;也就是说你在验…

MSB3541 Files 的值“<<<<<<< HEAD”无效。路径中具有非法字符。

MSB3541 Files 的值“<<<<<<< HEAD”无效。路径中具有非法字符。 一般来说出现这个问题是因为使用git版本控制工具合并代码出现了问题&#xff0c;想要解决也很简单。 如图点击错误后定位到文件&#xff0c;发现也没有什么问题。 根据错误后边的提示&a…

Peter算法小课堂—高精度减法

给大家看个小视频高精度减法_哔哩哔哩_bilibili 基本思想 计算机模拟人类做竖式计算&#xff0c;从而得到正确答案 大家还记得小学时学的“减法竖式”吗&#xff1f;是不是这样 x-y问题 函数总览&#xff1a; 1.converts() 字符串转为高精度大数 2.le() 判断大小 3.sub() …

苹果mac屏幕投屏镜像工具AirServer2024

airserver 是什么软件&#xff1f;AirServer 是一款 Airplay Mac屏幕镜像应用&#xff0c;AirServer可以通过 mac 实时接收iPhone、iPad以及Android设备的实时屏幕画面。AirServer 可以将一个简单的大屏幕或投影仪变成一个通用的屏幕镜像接收器。在您的大屏幕上启用 AirServer …

环境监测传感器守护我们的地球

随着人类活动的不断增加&#xff0c;环境问题日益凸显。为了更好地保护我们的地球&#xff0c;环境监测成为了一项非常重要的任务。而在这个领域&#xff0c;传感器技术发挥着至关重要的作用。今天&#xff0c;我们就来聊聊WX-WQX12 环境监测传感器。 环境监测传感器是一种能够…

同旺科技 USB 转 RS-485 适配器 -- 隔离型(定制款)

内附链接 1、USB 转 RS-485 适配器 隔离版主要特性有&#xff1a; ● 支持USB 2.0/3.0接口&#xff0c;并兼容USB 1.1接口&#xff1b; ● 支持USB总线供电&#xff1b; ● 支持Windows系统驱动&#xff0c;包含WIN10 / WIN11 系统32 / 64位&#xff1b; ● 支持Windows …

34 - 记一次线上SQL死锁事故:如何避免死锁?

之前我参与过一个项目&#xff0c;在项目初期&#xff0c;我们是没有将读写表分离的&#xff0c;而是基于一个主库完成读写操作。在业务量逐渐增大的时候&#xff0c;我们偶尔会收到系统的异常报警信息&#xff0c;DBA 通知我们数据库出现了死锁异常。 按理说业务开始是比较简…