Spring AOP与静态代理/动态代理

文章目录

  • 一、代理模式
    • 静态代理
    • 动态代理
    • 代理模式与AOP
  • 二、Spring AOP
    • Sping AOP用来处理什么场景
    • jdk 动态代理
    • cglib 动态代理
    • 面试题:讲讲Spring AOP的原理与执行流程
  • 总结


一、代理模式

代理模式是一种结构型设计模式,它允许对象提供替代品或占位符,以控制对这个对象的访问。代理对象通常充当客户端和实际服务对象之间的中介,以实现对服务对象的间接访问。
代理模式的实现有许多种方式,其中最常见的方式是静态代理和动态代理。

静态代理

静态代理是指,在编译期间就已经确定了代理类和目标类的关系,代理类和目标类的关系在程序运行之前就已经确定。下面是一个简单的静态代理模式示例:

假设有一个接口 IPrinter 表示打印机,它有一个 print 方法:

public interface IPrinter {void print(String document);
}

现在有一个实现了 IPrinter 接口的类 Printer

public class Printer implements IPrinter {@Overridepublic void print(String document) {System.out.println("打印机正在打印:" + document);}
}

现在我们想通过代理来记录打印机打印了哪些文件,我们可以创建一个代理类 PrinterProxy

public class PrinterProxy implements IPrinter {private IPrinter printer;public PrinterProxy(IPrinter printer) {this.printer = printer;}@Overridepublic void print(String document) {System.out.println("打印机开始工作,正在打印:" + document);printer.print(document);System.out.println("打印机打印完成。");}
}

PrinterProxy 类实现了 IPrinter 接口,并在 print 方法中调用真正的打印机的 print 方法,同时在这个方法之前和之后打印一些信息来记录打印机工作的情况。

现在我们可以使用以下代码来测试代理类的工作:

IPrinter printer = new Printer();
IPrinter printerProxy = new PrinterProxy(printer);printerProxy.print("茶叶蛋的前端简历");

运行程序后,输出的结果将是:

打印机开始工作,正在打印:茶叶蛋的前端简历
打印机正在打印:茶叶蛋的前端简历
打印机打印完成。

从输出结果可以看出,代理类 PrinterProxy 确实在调用真正的打印机类 Printerprint 方法之前和之后打印了一些信息。这是静态代理模式的基本实现方式。

结合我们生活来理解的话:最常见就是在大城市里租房了,房源太多,你忙于工作,此时你找了个代理人(上面👆的代理类) ,帮你处理找房子这个事(你的业务逻辑)。这里时候代理人就可以在找房子🏠这个事的前前后后(@defore @after @around 等)做文章了。例如,在找房子之前给你索取代理费(织入的逻辑)。找到房子帮你办理入住手续。

动态代理

Java提供了动态代理的支持,通过Java反射机制可以实现动态代理。我们可以使用Java自带的 java.lang.reflect.Proxy 类来实现动态代理。

以静态代理的打印机为例,我们可以使用动态代理来生成代理类。需要实现一个 InvocationHandler 接口,该接口包含一个 invoke 方法,我们可以在这个方法中实现代理的逻辑。

示例代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface IPrinter {void print(String document);
}class Printer implements IPrinter {@Overridepublic void print(String document) {System.out.println("打印机正在打印:" + document);}
}class PrinterHandler implements InvocationHandler {private Object target;public PrinterHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("打印机开始工作,正在打印:" + args[0]);Object result = method.invoke(target, args);System.out.println("打印机打印完成。");return result;}
}public class Main {public static void main(String[] args) {Printer printer = new Printer();PrinterHandler handler = new PrinterHandler(printer);IPrinter printerProxy = (IPrinter) Proxy.newProxyInstance(printer.getClass().getClassLoader(),printer.getClass().getInterfaces(),handler);printerProxy.print("茶叶蛋的简历");}
}

在此示例中,我们实现了 InvocationHandler 接口,并在 invoke 方法中实现了代理逻辑。在 main 方法中,我们首先创建了一个真正的打印机类 Printer 的实例,然后创建了一个 PrinterHandler 对象并将其传递给 Proxy.newProxyInstance 方法,该方法返回一个实现了 IPrinter 接口的代理类的实例。

在运行过程中,当使用代理类的 print 方法时,它将被拦截并调用 PrinterHandlerinvoke 方法,该方法将在真实的打印机类 Printerprint 方法之前和之后执行一些逻辑。

从输出结果可以看出,动态代理确实实现了和静态代理相同的代理逻辑。

打印机开始工作,正在打印:茶叶蛋的简历
打印机正在打印:茶叶蛋的简历
打印机打印完成。

代理模式与AOP

代理模式和AOP(面向切面编程)是两个不同的概念,但在某些方面存在相似之处。

代理模式是一种结构型设计模式,用于为其他对象提供一个替代或协助的代理对象,控制对原始对象的访问。代理对象通常具有与原始对象相同的接口,因此可以无缝地替换原始对象,但在访问原始对象时,代理对象可以执行额外的逻辑或限制,比如缓存对象或限制访问。

AOP是一种编程范式,用于将通用功能与应用程序的业务逻辑相分离。AOP通过在程序中定义切面(横切关注点)并在运行时将它们与各种连接点(方法调用、异常处理等)连接起来,实现了针对具体业务逻辑之外的通用功能的重用。

虽然代理模式可以在一定程度上实现AOP,但AOP是更高级别的概念,涵盖了更广泛的应用,包括动态代理、依赖注入、解耦等方面。因此,代理模式只是AOP的一种实现方式,而AOP更多地考虑了程序的整体结构和可维护性。

切面理解:
请添加图片描述

二、Spring AOP

2)使用AOP需要的一些概念。

1.通知(Advice)

通知定义了在切入点代码执行时间点附近需要做的工作。支持五种类型的通知:

Before(前) org.apringframework.aop.MethodBeforeAdvice
After-returning(返回后) org.springframework.aop.AfterReturningAdvice
After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
Arround(周围) org.aopaliance.intercept.MethodInterceptor
Introduction(引入) org.springframework.aop.IntroductionInterceptor

2.连接点(Joinpoint)
程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法调用时、异常抛出时、方法返回后等等。

3.切入点(Pointcut)

通知定义了切面要发生的“故事”,连接点定义了“故事”发生的时机,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定。

4.切面(Aspect)

通知、连接点、切入点共同组成了切面:时间、地点和要发生的“故事”。

5.引入(Introduction)

引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)。

6.目标(Target)

即被通知的对象,如果没有AOP,那么通知的逻辑就要写在目标对象中,有了AOP之后它可以只关注自己要做的事,解耦合

7.代理(proxy)
应用通知的对象,详细内容参见设计模式里面的动态代理模式。

8.织入(Weaving)
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:

(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器;

(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码;

(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术。

Sping AOP用来处理什么场景

下面是一个简单的基于Spring AOP的例子:

首先,定义一个接口UserService和实现类UserServiceImpl

public interface UserService {void addUser(User user);User getUser(int id);
}@Service
public class UserServiceImpl implements UserService {private Map<Integer, User> users = new HashMap<>();@Overridepublic void addUser(User user) {users.put(user.getId(), user);}@Overridepublic User getUser(int id) {return users.get(id);}
}

然后,定义一个切面LoggingAspect来记录方法的执行时间:

@Aspect
@Component
public class LoggingAspect {private Logger logger = LoggerFactory.getLogger(getClass());@Around("execution(* com.example.demo.UserService.*(..))")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object proceed = joinPoint.proceed();long executionTime = System.currentTimeMillis() - start;logger.info(joinPoint.getSignature() + " executed in " + executionTime + " ms");return proceed;}
}

在这个切面中,我们定义了一个@Around通知,用于环绕目标方法的执行。在执行方法前记录当前时间,执行方法后计算时间差,并将结果输出到日志中。

最后,在Spring配置文件中开启AOP:

<beans><context:component-scan base-package="com.example.demo"/><aop:aspectj-autoproxy/>
</beans>

使用<aop:aspectj-autoproxy>标签,Spring会自动查找所有被@Aspect注解标记的切面,并为它们创建代理对象。

现在,我们可以注入UserService并调用它的方法,来测试AOP是否生效:

@Controller
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/user/{id}")@ResponseBodypublic String getUser(@PathVariable int id) {userService.getUser(id);return "User retrieved";}@PostMapping("/user")@ResponseBodypublic String addUser(@RequestBody User user) {userService.addUser(user);return "User added";}
}

当我们访问/user/{id}/user时,控制台会输出类似于下面的日志:

com.example.demo.UserServiceImpl@xxxx executed in 10 ms

jdk 动态代理

JDK动态代理是一种在运行时动态生成代理类的机制,所以也被称为运行时代理。它主要涉及到以下两个核心类:

  1. java.lang.reflect.Proxy:该类是实现动态代理的关键类,它提供了构造代理类实例所需的方法。
  2. java.lang.reflect.InvocationHandler:该接口是实际处理代理对象方法调用的地方,通过调用它的 invoke() 方法实现代理对象的方法调用。

JDK动态代理的实现原理:

  • 定义一个接口或者抽象类,指定了需要被代理的方法。
  • 创建一个实现 InvocationHandler 接口的类,它需要实现 invoke() 方法,该方法是代理类的调用处理器,负责处理被代理对象的方法调用。
  • 通过 Proxy 类的静态方法 newProxyInstance() 获取代理对象实例,该方法接收三个参数:ClassLoader、Class[] 和 InvocationHandler。其中,ClassLoader 是代理类的 ClassLoader,Class[] 是指定被代理类实现的接口列表,InvocationHandler 是实现了 invoke() 方法的调用处理器对象。
  • 当代理对象的方法被调用时,代理对象会调用 InvocationHandler 实例的 invoke() 方法。该方法使用 Method 对象来调用实际的被代理对象的方法,并返回结果。

总体来说,JDK动态代理的实现过程涉及到反射和动态生成类的机制,它可以在运行时创建代理对象,并将代理对象的方法调用转发给 InvocationHandler 接口的实现类处理。

cglib 动态代理

CGLIB(Code Generation Library)是一个基于ASM(一个 Java 字节码操作框架)的代码生成库,它可以在运行时动态生成字节码,从而实现动态代理、AOP 等功能。

相比于 Java 中的 JDK 动态代理,CGLIB 动态代理具有以下特点:

  1. JDK 动态代理只能代理接口,而 CGLIB 可以代理普通类;
  2. JDK 动态代理调用代理方法时需要通过反射调用,而 CGLIB 利用字节码技术直接调用方法,性能更高。

CGLIB 动态代理的基本原理是继承原始类或实现接口,并在子类中重写原始类或接口的方法,在方法中添加前置、后置等代理逻辑。CGLIB 动态代理一般使用 Enhancer 类来实现,其核心 API 包括:

  1. setSuperclass:设置被代理类的父类;
  2. setCallback:设置回调对象;
  3. create:创建代理对象。

使用 CGLIB 动态代理可以实现更加灵活的代理逻辑,但是代理对象的创建和调用会消耗更多的资源,需要根据实际情况进行使用。

面试题:讲讲Spring AOP的原理与执行流程

Spring AOP(面向切面编程)是一种通过动态代理或字节码增强等技术,在程序运行期间对指定方法进行增强的技术。Spring AOP的原理是基于动态代理技术,利用Java的反射机制,在不改变原有代码的情况下,对指定方法进行增强。它提供了一种使程序横向通用化的能力,比如事务管理、日志记录等功能。在Spring AOP中,切面是应用横向关注点的一种特殊对象,而横向关注点是指跨越应用程序多个接口的功能或行为,比如安全,事务,日志等。

执行流程如下:

首先,程序通过配置文件或注解,定义好需要被增强的方法,以及增强的方式。Spring AOP提供了五个增强类型,分别是前置增强(Before Advice),后置增强(After Advice),环绕增强(Around Advice),异常抛出增强(After-Throwing Advice)和最终增强(After-Finally Advice

然后,Spring框架在程序运行期间,根据这些定义,动态生成一个代理对象

当程序调用被增强的方法时,代理对象会先调用相应的增强方法,然后再执行被增强的方法。

在增强方法中,可以进行一些额外的处理,例如记录日志、验证权限、性能统计等。

最后,程序返回执行结果。


总结

我们一开始简单的初步认识了下代理模式,其中常见的实现方式有静态代理与动态代理,同时写了打印机🖨️的调用时机代码。接着我们思考🤔代理模式和我们使用spring aop有什么联系?简单的了解aop 与spring aop的实现方式。 最后我们比较了jdk 与cglib 的代理模式 ,接着简单过一遍Spring aop 的原理与执行流程。

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

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

相关文章

肖sir__设计测试用例方法之边界值03_(黑盒测试)

设计测试用例方法之边界值 边界点定义 上点&#xff1a;边界上的点 离点&#xff1a;离上点最近的点&#xff08;即上点左右两边最邻近的点&#xff09; 内点&#xff1a;在域范围内的点 案例&#xff1a;qq号&#xff1a;5-12位 闭区间&#xff1a; 离点&#xff1a;5 位 &…

KC705开发板——MGT IBERT测试记录

本文介绍使用KC705开发板进行MGT的IBERT测试。 KC705开发板 KC705开发板的图片如下图所示。FPGA芯片型号为XC7K325T-2FFG900C。 MGT MGT是 Multi-Gigabit Transceiver的缩写&#xff0c;是Multi-Gigabit Serializer/Deserializer (SERDES)的别称。MGT包含GTP、GTX、GTH、G…

Calico IP In IP模拟组网

Calico IP In IP模拟组网 网络架构 模拟组网 先在k8s-master-1节点执行如下命令&#xff1a; # 创建veth-pair设备对ip link add veth1 type veth peer name eth0# 创建ns1网络命名空间ip netns add ns1# 将eth0网卡插入ns1网络命名空间ip link set eth0 netns ns1# 为ns1网…

CC-TDOB01 CC-TDIL01 有效地监控和管理热工设备

CC-TDOB01 CC-TDIL01 有效地监控和管理热工设备 新兴的互联工厂技术是霍尼韦尔启动其43的亮点注册营养师霍尼韦尔用户组(HUG)美洲研讨会&#xff0c;重点是向制造商展示数字化转型如何帮助他们实现更高水平的高性能。 来自石油和天然气、化工、纸浆和造纸以及金属和采矿行业的…

全国唯一一所初试考Java的学校!平均300分拿下

苏州科技大学 考研难度&#xff08;☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1187字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 苏州科技…

大数据组件Sqoop-安装与验证

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

19-springcloud(上)

一 微服务架构进化论 单体应用阶段 (夫妻摊位) 在互联网发展的初期&#xff0c;用户数量少&#xff0c;一般网站的流量也很少&#xff0c;但硬件成本较高。因此&#xff0c;一般的企业会将所有的功能都集成在一起开发一个单体应用&#xff0c;然后将该单体应用部署到一台服务器…

实现Android APK瘦身99.99%

摘要&#xff1a; 如何瘦身是 APK 的重要优化技术。APK 在安装和更新时都需要经过网络下载到设备&#xff0c;APK 越小&#xff0c;用户体验越好。本文作者通过对 APK 内在机制的详细解析&#xff0c;给出了对 APK 各组成成分的优化方法及技术&#xff0c;并实现了一个基本 APK…

【PyGame/PyQy5】get-start 快速入门

1.windows、linux&#xff0c;mac 安装python3 2.PC系统安装pygame, 指令&#xff1a;pip install pygame 3. 保存如下文件&#xff1a; test_game.py 4.PC上运行 python test_game.py import pygame# 初始化Pygame pygame.init()# 创建游戏窗口 window pygame.display.s…

语言深入理解指针(非常详细)(三)

目录 数组名的理解使用指针访问数组 一维数组传参的本质二级指针指针数组指针数组模拟二维数组 数组名的理解 在上⼀个章节我们在使用指针访问数组的内容时&#xff0c;有这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0];这里我们使用 &am…

史上最全PMP学习资料、项目管理资料、备考经验包,3A一次通过

你是否也有过类似的经历&#xff1f; 为了获取备考资料&#xff0c;有的同学在论坛、知乎或者相关垂直类网站下载了很多的资料&#xff0c;这些资料大部分是机构进行获客引流的资料&#xff0c;没有真正的干货。 经常会看到10G、20G的资料包&#xff0c;感觉内容很丰富&#xf…

【LeetCode算法系列题解】第51~55题

CONTENTS LeetCode 51. N 皇后&#xff08;困难&#xff09;LeetCode 52. N 皇后 II&#xff08;困难&#xff09;LeetCode 53. 最大子序和&#xff08;中等&#xff09;LeetCode 54. 螺旋矩阵&#xff08;中等&#xff09;LeetCode 55. 跳跃游戏&#xff08;中等&#xff09; …

智慧电力系统

智慧电力系统依托电易云-智慧电力物联网&#xff0c;将人工智能技术、物联网技术与电力行业相结合&#xff0c;通过数据的收集、分析和应用&#xff0c;实现电力资源精准调度、安全稳定运行、智能管理的全过程解决方案。 智慧电力系统的核心理念是将科技与能源紧密结合&#x…

pycharm创建的虚拟环境为什么用conda env list命令查询不到?

问题描述&#xff1a;pycharm创建的虚拟环境为什么用conda env list命令查询不到。 pycharm开发环境可以创建虚拟环境&#xff0c;目的是为隔绝其他环境种库带来的版本干扰&#xff0c;但是发现一个问题&#xff0c;无论是在windows终端、anaconda终端、Pycharm开发环境中的终…

【Spring】SpringBoot的10个参数验证技巧

这里写目录标题 前言1.使用验证注解2 使用自定义验证注解3 在服务器端验证4 提供有意义的错误信息5 将 i18n 用于错误消息messages.properties6 使用分组验证7 对复杂逻辑使用跨域验证8 对验证错误使用异常处理9 测试你的验证逻辑10 考虑客户端验证总结 前言 参数验证很重要&am…

第一次做接口测试用例

一、简介 在开始接口测试之前&#xff0c;我们想一下&#xff0c;接口测试的流程是什么&#xff1f;说到这里&#xff0c;有些人就会产生好奇和疑问&#xff0c;心里mmp&#xff1a;接口测试要什么流程哈&#xff1f;&#xff1f;&#xff1f;不就是参考接口文档&#xff0c;直…

如何通过构建遥感光谱反射信号与地表参数之间的关系模型来准确估算植被参数?植被参数光学遥感反演方法(Python)及遥感与生态模型数据同化算法

目录 专题一 植被参数遥感反演理论 专题二 植被叶片及冠层反射率模拟与处理 专题三 植被遥感模型参数敏感性分析 专题四 基于查找表(LUT)方法反演植被参数 专题五 基于优化算法反演植被参数 专题六 基于机器学习反演植被参数 专题七 遥感数据同化理论 专题八 同化遥感反…

MASM32编程状态栏显示字符动画,按钮跑马灯

一、需求分析 由于sysInfo扫描的内容比较多&#xff0c;打算为它增加一点动画效果&#xff0c;提醒用户程序正在运行&#xff0c;耐心等待。 二、构建测试窗口 测试窗口上放置有一个按钮&#xff0c;按钮上的初始文字是“开始扫描”&#xff1b;并使用状态栏&#xff0c;状态…

视频垂直镜像播放,为您的影片带来新鲜感

大家好&#xff01;在制作视频时&#xff0c;我们常常希望能够给观众带来一些新鲜感和独特的视觉效果。而垂直镜像播放是一个能够让您的影片与众不同的技巧。然而&#xff0c;传统的视频剪辑软件往往无法直接实现视频的垂直镜像播放&#xff0c;给我们带来了一些困扰。现在&…

基于Python的IOS自动化测试环境搭建

文章目录 一、测试架构介绍1.1 WebDriverAgent原理分析1.2 tidevice原理分析二、环境安装2.1 iOS 设备安装 WebDriverAgent2.2 安装iTunes2.3 安装tidevice2.4 安装facebook-wda自动化三、操作流程四、Weditor的安装和使用一、测试架构介绍 以下为测试架构原理图 手机端的WDA…