【Spring】Spring AOP 初识及实现原理解析

  • 博主简介:想进大厂的打工人
  • 博主主页:@xyk:
  • 所属专栏: JavaEE进阶 

目录

文章目录

一、初识AOP

1.1 什么是AOP?

1.2 AOP的组成

1.2.1 切面(Aspect)

1.2.2 切点(Pointcut)

1.2.3 连接点(Join Point)

1.2.4 通知(Advice)

1.3 AOP的使用场景

二、Srping AOP 实现

2.1 添加Spring AOP 依赖

2.2 定义切面和切点

2.3 定义通知

三、Spring AOP 实现原理

3.1 什么是动态代理?

3.2 JDK 动态代理实现

3.3 CGLIB 动态代理实现

3.4 JDK 和 CGLIB 实现的区别


一、初识AOP

1.1 什么是AOP?

AOP(Aspect Oriented Programming):面向切面编程,它是⼀种思想,它是对某⼀类事情的
集中处理。
在我们想要对某一件事情进行集中处理,就可以使用到AOP,它提供一种将程序中的横切关注点模块化的方式。在 AOP 中,我们将这些横切关注点称为“切面”,它们独立于业务逻辑模块,但是可以在程序运行的不同阶段被织入到业务逻辑中。

简单来说,AOP 就是对某一件事进行集中处理的思想方式~

1.2 AOP的组成

1.2.1 切面(Aspect)

切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包
括了连接点的定义。相当于处理某方面具体问题的一个类,包含多个方法,而这些方法就是切点和通知。

1.2.2 切点(Pointcut)

Pointcut 的作⽤就是提供⼀组规则来匹配连接点(Join Point),给满足规则的连接点添加通知(Advice),可以理解为用来进行主动拦截的规则(配置)

1.2.3 连接点(Join Point)

应⽤执⾏过程中能够插⼊切⾯的⼀个点,连接点可以理解为可能会触发AOP规则的所有点。(所有请求)

1.2.4 通知(Advice)

在AOP术语中,切面的工作被称之为通知。通知是切面在连接点上执行的动作。它定义了在何时(例如在方法调用之前或之后)以及如何(例如打印日志或进行性能监控)应用切面的行为。即,程序中被拦截请求触发的具体动作。

Spring 切⾯类中,可以在方法上使⽤以下注解,会设置⽅法为通知方法,在满⾜条件后会通知本
⽅法进⾏调⽤:

  1. 前置通知使⽤ @Before:通知⽅法会在⽬标⽅法调⽤之前执行。
  2. 后置通知使⽤ @After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。
  3. 返回之后通知使⽤ @AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
  4. 抛异常后通知使⽤ @AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
  5. 环绕通知使⽤ @Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执行⾃定义的行为。

1.3 AOP的使用场景

在做任何一个系统都需要登录功能,那么几乎想要使用这个系统都需要我们进行验证用户登录状态,我们之前的处理⽅式是每个 Controller 都要写⼀遍⽤户登录验证,然⽽当你的功能越来越多,那么你要写的登录验证也越来越多,⽽这些⽅法⼜是相同的,这么多的⽅法就会代码修改和维护的成本。对于这种功能统⼀,且使⽤的地⽅较多的功能,就可以考虑 AOP来统⼀处理了。

 

除了统一登录判断外,使用AOP还可以实现:

  • 用户登录验证
  • 统⼀⽇志记录
  • 统⼀⽅法执⾏时间统计
  • 统⼀的返回格式设置
  • 统⼀的异常处理
  • 事务的开启和提交等

二、Srping AOP 实现

Spring AOP 的实现步骤如下:

  1. 添加 Spring AOP 框架⽀持
  2. 定义切⾯和切点:(1)创建切面类(2)配置拦截规则
  3. 定义通知

2.1 添加Spring AOP 依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-bo
ot-starter-aop --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

2.2 定义切面和切点

使用 @Aspect 注解表明当前类为一个切面,而在切点中,我们要定义拦截的规则,具体实现如下:

@Component // 随着框架的启动而启动
@Aspect // 告诉框架我是一个切面类
public class UserAspect {// 定义切点(配置拦截规则)@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut(){}
}

在上述实现代码中,pointcut 为一个空方法,只是起到一个“标识”的作用,标识下面的通知方法具体指的是哪个切点,切点可以有多个。

切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法为:

execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
修饰符和异常可以省略

常见的切点表达式的示例:

  • 匹配特定类的所有方法:
  • execution(* com.example.MyClass.*(..)):匹配 com.example.MyClass 类中的所有方法。
  • 匹配特定包下的所有方法:
  • execution(* com.example.*.*(..)):匹配 com.example 包及其子包下的所有方法。
  • 匹配特定方法名的方法:
  • execution(* com.example.MyClass.myMethod(..)):匹配 com.example.MyClass 类中名为 myMethod 的方法。
  • 匹配特定方法参数类型的方法:
  • execution(* com.example.MyClass.myMethod(String, int)):匹配 com.example.MyClass 类中具有一个 String 参数和一个 int 参数的 myMethod 方法。
  • 匹配特定返回类型的方法:
  • execution(String com.example.MyClass.myMethod(..)):匹配 com.example.MyClass 类中返回类型为 String 的 myMethod 方法。
package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/hi")public String sayHi(String name){System.out.println("执行了Hi");return "Hi," + name;}@RequestMapping("/hello")public String sayHello(){System.out.println("执行了Hello");return "Hello,world";}
}

2.3 定义通知

通知定义的是被拦截方法具体要执行的业务。我们上面列出了可以使用哪些通知~这里举出例子

package com.example.demo.aop;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component // 随着框架的启动而启动
@Aspect // 告诉框架我是一个切面类
public class UserAspect {// 定义切点(配置拦截规则)@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut(){}@Before("pointcut()")public void beforeAdvice(){System.out.println("执行了前置通知~");}@After("pointcut()")public void AfterAdvice(){System.out.println("执行了后置通知~");}/*** 环绕通知* @param joinPoint* @return*/@Around("pointcut()")public Object aroundAdvice(ProceedingJoinPoint joinPoint){System.out.println("进入了环绕通知~");Object obj = null;try {// 执⾏拦截⽅法obj = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("退出了环绕通知~");return obj;}}

环绕通知是在前置通知之前和后置通知之后运行的~

  

三、Spring AOP 实现原理

Spring AOP 是通过动态代理的⽅式,在运⾏期将 AOP 代码织⼊到程序中的,它的实现⽅式有两种:JDK Proxy 和 CGLIB因此,Spring 对 AOP 的支持局限于方法级别的拦截。

  1. 默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类
  2. 没有实现接⼝的类,会基于 CGLIB ⽣成代理类
     

3.1 什么是动态代理?

动态代理(Dynamic Proxy)是一种设计模式,它允许 在运行时创建代理对象,并将方法调用转发给实际的对象。 动态代理可以用于实现横切关注点(如日志记录、性能监控、事务管理等)的功能,而无需修改原始对象的代码。

在Java中,动态代理通常使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。

调用者在调用方法时,会先转发给代理类创建的代理对象,随后再由代理对象转发给目标对象。

以下是使用动态代理的一般步骤:

  1. 创建一个实现InvocationHandler接口的类,该类将作为代理对象的调用处理程序。在InvocationHandler接口的invoke方法中,可以定义在方法调用前后执行的逻辑。
  2. 使用Proxy类的newProxyInstance方法创建代理对象。该方法接受三个参数:类加载器、代理接口数组和调用处理程序。它将返回一个实现指定接口的代理对象。
  3. 使用代理对象调用方法。当调用代理对象的方法时,实际上会调用调用处理程序的invoke方法,并将方法调用转发给实际的对象。
     

3.2 JDK 动态代理实现

先通过实现 InvocationHandler 接⼝创建⽅法调⽤处理器,再通过 Proxy 来创建代理类。

import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理:使⽤JDK提供的api(InvocationHandler、Proxy实现),此种⽅式实现,要求被代理类必须实现接⼝
public class PayServiceJDKInvocationHandler implements InvocationHandler {//⽬标对象即就是被代理对象private Object target;public PayServiceJDKInvocationHandler( Object target) {this.target = target;}//proxy代理对象@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//1.安全检查System.out.println("安全检查");//2.记录⽇志System.out.println("记录⽇志");//3.时间统计开始System.out.println("记录开始时间");//通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args);//4.时间统计结束System.out.println("记录结束时间");return retVal;}public static void main(String[] args) {PayService target= new AliPayService();//⽅法调⽤处理器InvocationHandler handler =new PayServiceJDKInvocationHandler(target);//创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建PayService proxy = (PayService) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{PayService.class},handler);proxy.pay();}
}

3.3 CGLIB 动态代理实现

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;
import java.lang.reflect.Method;public class PayServiceCGLIBInterceptor implements MethodInterceptor {//被代理对象private Object target;public PayServiceCGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//1.安全检查System.out.println("安全检查");
//2.记录⽇志System.out.println("记录⽇志");
//3.时间统计开始System.out.println("记录开始时间");
//通过cglib的代理⽅法调⽤Object retVal = methodProxy.invoke(target, args);
//4.时间统计结束System.out.println("记录结束时间");return retVal;}public static void main(String[] args) {PayService target= new AliPayService();PayService proxy= (PayService) Enhancer.create(target.getClass(),new PayServiceCGLIBInterceptor(target));proxy.pay();}
}

3.4 JDK 和 CGLIB 实现的区别

  1. JDK 实现,要求被代理类必须实现接口, 之后是通过 InvocationHandler 及 Proxy,在运⾏时动态的在内存中⽣成了代理类对象,该代理对象是通过实现同样的接⼝实现(类似静态代理接⼝实现的⽅式),只是该代理类是在运⾏期时,动态的织⼊统⼀的业务逻辑字节码来完成。
  2. CGLIB 实现,被代理类可以不实现接口, 是通过继承被代理类,在运⾏时动态的⽣成代理类对象。

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

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

相关文章

blender基础认识(选项开关、工具栏、视图等)

文章目录 引言一、大纲选项开关和保存启动文件1. 大纲选项1. 禁用选中2. 视图影藏3. 视图禁用4. 渲染禁用 2. 保存启动文件 二、工具栏和侧边栏1. 左侧工具栏2. 右侧工具栏 三、视图1. 视角2. 缩放3. 拖拽4. 摄像机视角5. 切换正交视图6. 局部视图7. 显示隐藏 四、添加删除物体…

【DMA】如何保证 DMA 和 cache 的一致性

一方面&#xff0c;当 CPU 要从cache 读取数据时&#xff0c;会先检查cache是否命中&#xff0c;如果命中就直接返回&#xff0c;此时便不再访问内存&#xff1b;另一方面&#xff0c;DMA 在 向内存写入数据。这样一来就造成了DMA 传输的内容和cache中缓存的内容不一致。 DMA 向…

关于echarts遇到的一些问题

1.echarts监听legend&#xff0c;动态设置legend属性无效 动态更改legend中的icon&#xff0c; myChart.setOption(option&#xff09;失效&#xff0c;但是设置局部就生效 myChart.on(legendselectchanged, function (params) {if (params.selected[params.name]) {data1[dat…

C++ 多态性——纯虚函数与抽象类

抽象类是一种特殊的类&#xff0c;它为一个类族提供统一的操作界面。抽象类是为了抽象和设计的目的而建立的。可以说&#xff0c;建立抽象类&#xff0c;就是为了通过它多态地使用其中的成员函数。抽象类处于类层次的上层&#xff0c;一个抽象类自身无法实例化&#xff0c;也就…

VUE+view table.exportCsv()导出.csv文档时如何防止数据格式为科学计数

当使用table.exportCsv()方法导出数据时&#xff0c;出现科学计数法问题&#xff0c;像电话号码&#xff0c;身份证号码等&#xff0c;当数据大于15位后面的会用0替代。 针对这一问题&#xff0c;解决方法如下&#xff1a;就是再数字前加上制表符“\t”注意双引号&#xff0c;…

前端技术基础-css

前端技术基础-css【了解】 一、css理解 概念&#xff1a;CSS&#xff1a;C(cascade) SS(StyleSheet) &#xff0c;级联样式表。作用&#xff1a;对网页提供丰富的视觉效果&#xff0c;进行美化页面(需要在html页面基础上)样式规则&#xff1a;样式1&#xff1a;值1;样式2&…

0基础学习VR全景平台篇 第79篇:全景相机-泰科易如何直播推流

泰科易科技是中国的一家研发全景相机的高科技公司&#xff0c;前不久&#xff0c;在2020世界VR产业大会上发布了新一代5G VR直播影像采集终端--360starlight。以其出色的夜景成像效果和一“部”到位的直播方案重新定义了VR慢直播相机&#xff0c;对行业具有高度借鉴意义。 本文…

【工具插件类教学】电脑端移动端缩放大图自适应Simple Zoom

目录 简介 1.创建Canvas并设置 2.使用预制体Zoom 3.商店地址 简介 特点: •易于使用和高度可定制。 •支持鼠标(桌面)和触摸(移动)。 •指定最小和最大缩放的限制。 •缩放指针(鼠标/手指)或屏幕上预定义的自定义位置。 •变焦时使用夹紧/弹性变焦类型。 •定义缩…

MySQL插入数据的方法

插入数据方法&#xff1a; 1.insert into 表 values(value1, value2, value3....) 2.insert into 表 (字段1&#xff0c; 字段3&#xff0c; 字段5) values(value1, value2, value3) 3.insert into 表 [(字段1&#xff0c; 字段2&#xff0c; 字段3....)] values(value1, val…

【CSS】网格布局(简单布局、网格合并、网格嵌套)

文章目录 CSS网格布局&#xff08;Grid Layout&#xff09;1. 简单布局2. 网格合并3. 网格嵌套4. 总结 CSS网格布局&#xff08;Grid Layout&#xff09; CSS网格布局&#xff08;Grid Layout&#xff09;是一种强大且灵活的CSS布局系统&#xff0c;允许开发者以网格形式组织和…

Spring源码解析(八):bean后置处理器CommonAnnotationBeanPostProcessor

Spring源码系列文章 Spring源码解析(一)&#xff1a;环境搭建 Spring源码解析(二)&#xff1a;bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三)&#xff1a;bean容器的刷新 Spring源码解析(四)&#xff1a;单例bean的创建流程 Spring源码解析(五)&…

opencv基础-34 图像平滑处理-双边滤波cv2.bilateralFilter()

双边滤波&#xff08;BilateralFiltering&#xff09;是一种图像处理滤波技术&#xff0c;用于平滑图像并同时保留边缘信息。与其他传统的线性滤波方法不同&#xff0c;双边滤波在考虑像素之间的空间距离之外&#xff0c;还考虑了像素之间的灰度值相似性。这使得双边滤波能够有…

数据结构初阶--二叉树的顺序结构之堆

目录 一.堆的概念及结构 1.1.堆的概念 1.2.堆的存储结构 二.堆的功能实现 2.1.堆的定义 2.2.堆的初始化 2.3.堆的销毁 2.4.堆的打印 2.5.堆的插入 向上调整算法 堆的插入 2.6.堆的删除 向下调整算法 堆的删除 2.7.堆的取堆顶元素 2.8.堆的判空 2.9.堆的求堆的…

[Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序

1.今天开发了一套服务程序&#xff0c;使用的是Odbc连接MySql数据库&#xff0c; 在我本机用VS打开程序时&#xff0c;访问一切正常&#xff0c;当发布出来装在电脑上&#xff0c;连接数据库时提示&#xff1a; [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定…

VoxWeekly|The Sandbox 生态周报|20230731

欢迎来到由 The Sandbox 发布的《VoxWeekly》。我们会在每周发布&#xff0c;对上一周 The Sandbox 生态系统所发生的事情进行总结。 如果你喜欢我们内容&#xff0c;欢迎与朋友和家人分享。请订阅我们的 Medium 、关注我们的 Twitter&#xff0c;并加入 Discord 社区&#xf…

Zebec Protocol 将进军尼泊尔市场,通过 Zebec Card 推动该地区金融平等

流支付正在成为一种全新的支付形态&#xff0c;Zebec Protocol 作为流支付的主要推崇者&#xff0c;正在积极的推动该支付方案向更广泛的应用场景拓展。目前&#xff0c;Zebec Protocol 成功的将流支付应用在薪酬支付领域&#xff0c;并通过收购 WageLink 将其纳入旗下&#xf…

C#实现SqlServer数据库同步

实现效果&#xff1a; 设计思路&#xff1a; 1. 开启数据库及表的cdc&#xff0c;定时查询cdc表数据&#xff0c;封装sql语句(通过执行类型&#xff0c;主键;修改类型的cdc数据只取最后更新的记录)&#xff0c;添加到离线数据表&#xff1b; 2. 线程定时查询离线数据表&#xf…

有哪些常用的设计素材网站?

素材网站可以是设计师和创意人员的灵感来源。这些网站收集了各种类型的平面设计图片&#xff0c;包括标志、海报、网站设计、包装设计、插图等。在本文中&#xff0c;我将推荐15个平面设计图素材网站&#xff0c;以帮助您找到新的想法和灵感。 1.即时设计资源社区 即时设计资…

SpringBoot 热部署

文章目录 前言一、spring-boot-devtools添加热部署框架支持settings 开启项目自动编译开启运行中热部署使用Debug启动 二、IDEA 自带 HowSwap 功能设置 Spring Boot 启动类等待项目启动完成点击热加载按钮存在的问题 三、JRebel 插件【推荐】安装插件使用插件 前言 在日常开发…

分布式协议与算法——CAP理论、ACID理论、BASE理论

CAP理论 CAP理论&#xff0c;对分布式系统的特性做了高度抽象&#xff0c;比如抽象成了一致性、可用性和分区容错性&#xff0c;并对特性间的冲突&#xff08;也就是CAP不可能三角&#xff09;做了总结。 CAP三指标 CAP理论对分布式系统的特性做了高度抽象&#xff0c;形成了…