Spring中的AOP

目录

一.什么是AOP?

二.SpringAOP的简单实现

三.Spring AOP 详解

1.切点(Pointcut)

切点的定义方式

切点表达式的语法

1) execution表达式

1. *(星号)

2. ..(两个点)

3. +(加号)

2)@annotation

2.连接点(Join Point)

3.通知(Advice)

特点和分类

4.切面(Aspect)

四.Spring AOP 的辅助注解

1. @PointCut注解

2. @Order注解

五.Spring AOP 原理


一.什么是AOP?

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在使横切关注点的实现更加模块化和可维护横切关注点是那些影响应用程序中多个类或模块的功能,例如日志记录、事务管理、安全性和缓存等。

AOP通过将横切关注点从它们所影响的对象中分离出来,然后以一种更模块化的方式将其应用于应用程序的组件中。这样可以避免将相同的代码散布在整个应用程序中,提高了代码的可重用性和可维护性。

(AOP是⼀种思想, 它的实现方法有很多, 而我们要介绍的Spring AOP是其中的⼀种实现方式)


二.SpringAOP的简单实现

在具体介绍SpringAOP之前,我们先简单实现一个AOP来统计方法的执行时间.

1)引入AOP相关的配置

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2)引入作为例子的实体类Cal(计算器)

@Component
public class Cal {//加法运算public int add(int num1,int num2){return num1+num2;}//减法运算public int sub(int num1,int num2){return num1-num2;}//乘法运算public int mul(int num1,int num2){return num1*num2;}//除法运算public int div(int num1,int num2){return num1/num2;}}

3)实现关于时间统计的AOP类

@Component
@Aspect
public class TimeAspect {@Around("execution(* com.example.demo.aop.Cal.*(..))")public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {//记录开始时间long begin=System.currentTimeMillis();//执行目标类的方法Object result=pjp.proceed();//记录结束时间long end=System.currentTimeMillis();//打印结果System.out.println(pjp.getSignature().getName()+"方法的执行时间为:"+(end-begin));//返回原方法的结果return result;}
}
  •  @Aspect: 标识这是⼀个切面类
  •  @Around: 环绕通知, 在目标方法的前后都会被执行,后面的表达式表示对哪些方法进行增强.
  •   ProceedingJoinPoint.proceed() 让原始方法执行

4)在Test类中进行测试

@SpringBootApplication
public class Test {public static void main(String[] args) {//获取IOC容器ApplicationContext context=new AnnotationConfigApplicationContext("com.example.demo.aop");//获取容器里的代理类Cal cal=context.getBean(Cal.class);//运行代理类中的方法System.out.println("结果为"+cal.add(5, 4));}
}结果为:add方法的执行时间为:8结果为9Process finished with exit code 0

@SpringBootApplication:

该注解本质上是以下三个注解的组合:

  • @Configuration: 表示这个类可以使用Spring IoC容器作为bean定义的来源。
  • @EnableAutoConfiguration: 告诉Spring Boot根据添加的jar依赖自动配置Spring应用程序。
  • @ComponentScan: 告诉Spring在该包及其子包中寻找其他组件、配置和服务

在你的主类(通常是包含main方法的类)上使用@SpringBootApplication注解,Spring Boot会自动扫描和配置你的组件。


三.Spring AOP 详解

1.切点(Pointcut)

在面向切面编程(AOP)中,切点(Pointcut)是指你希望在应用程序中插入横切关注点的位置的定义。换句话说,切点确定了哪些方法调用或类会触发切面的逻辑执行。在Spring中,切点用于定义那些需要被AOP增强的方法集合。

切点的定义方式

在Spring中,切点通常使用切点表达式(Pointcut Expression)来定义,切点表达式指定了切点的匹配规则,可以基于方法的名称、返回类型、参数类型等来选择特定的方法。切点表达式的语法类似于正则表达式,但更专注于描述方法的签名和执行位置。

切点表达式的语法

Spring的切点表达式语法基于AspectJ表达式语言(AspectJ Expression Language)。常见的切点表达式语法包括:

  • execution: 匹配方法执行的连接点,是最常用的切点表达式。

    • 示例:execution(* com.example.service.*.*(..)) 匹配com.example.service包下所有类的所有方法。
  • within: 匹配指定类型内的所有方法执行。

    • 示例:within(com.example.service.*) 匹配com.example.service包及其子包内所有类的所有方法。
  • args: 匹配指定参数类型的方法执行。

    • 示例:args(java.lang.String) 匹配方法有一个String类型参数的执行。
  • @annotation: 匹配使用了特定注解的方法执行。

    • 示例:@annotation(org.springframework.transaction.annotation.Transactional) 匹配所有使用了@Transactional注解的方法。
  • bean: 匹配指定bean名称或类型的方法执行。

    • 示例:bean(userService) 匹配名称为userService的bean的所有方法。

在上面AOP的实现例子中,切点指的就是@Around注解后面的部分

("execution(* com.example.demo.aop.Cal.*(..))")

意思为:这个切点表达式匹配了com.example.demo.aop包中名为Cal的所有类的所有方法

切点表达式中最常用的两种表达方式为:

  • execution: 匹配方法执行的连接点
  • @annotation: 匹配使用了特定注解的方法执行

我们将详细介绍这两种方式的表达方式

1) execution表达式

作为匹配方法的表达式,语法为:

execution(<访问修饰符> <返回类型> <包名.类名.⽅法(⽅法参数)> <异常>)execution(* com.example.demo.aop.Cal.*(..))//其中:访问修饰符和异常可以省略

切点表达式支持通配符表达: 

1. *(星号)
  • 作用* 通配符用来匹配任意数量、任意字符的部分。
  • 位置: 可以用于方法修饰符、返回类型、类名、方法名以及参数类型的任意部分。
  • 示例:
    execution(* com.example.service.*.*(..)):匹配 com.example.service 包中任意类的任意方法。execution(public * *(..)):匹配任意公有方法。execution(* get*()):匹配以 get 开头且没有参数的方法。
2. ..(两个点)
  • 作用.. 通配符用来匹配任意数量的子包或者方法参数。
  • 位置: 主要用于包路径和方法参数的部分。
  • 示例:
    execution(* com.example..*.*(..)):匹配 com.example 包及其子包中任意类的任意方法。execution(* save*(..)):匹配方法名以 save 开头的方法,可以有任意数量的参数。
3. +(加号)
  • 作用+ 通配符用于匹配指定类及其子类中的方法。
  • 位置: 只能用于类名部分,不能用于其他位置。
  • 示例:
    execution(* com.example.service.SomeService+.*(..)):匹配 SomeService 类及其子类中的任意方法。

组合使用示例

execution(public * com.example..*Service.*(..)):匹配 com.example 包及其子包中所有类名以 Service 结尾的公共方法。execution(* delete*(Long, ..)):匹配方法名以 delete 开头,第一个参数为 Long 类型,第二个及其后的参数任意类型的方法。

2)@annotation

@annotation适合匹配多个无规则的方法,它的实现流程是:

  1. 编写自定义注解
  2. 使用 @annotation 表达式来描述切点 
  3. 在连接点的方法上添加自定义注解

示例如下:

1.定义自己的@annotation类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}//@Target 标识了Annotation 所修饰的对象范围,即该注解可以⽤在什么地⽅//@Retention 指Annotation被保留的时间⻓短,标明注解的⽣命周期

2.在切面类中使用 @annotation 切点表达式定义切点

@Component
@Aspect
public class TimeAspect {//com.example.demo.aop.MyAnnotation表示对aop包的所有添加了@MyAnnotation的方法生效@Around("@annotation(com.example.demo.aop.MyAnnotation)")public Object recordTime2(ProceedingJoinPoint pjp) throws Throwable {//记录开始时间long begin=System.currentTimeMillis();//执行目标类的方法Object result=pjp.proceed();//记录结束时间long end=System.currentTimeMillis();//打印结果System.out.println(pjp.getSignature().getName()+"方法的执行时间为:"+(end-begin));//返回原方法的结果return result;}}

 3.在要生效的方法中添加自己定义的注解

@Component
public class Cal {//加法运算@MyAnnotationpublic int add(int num1,int num2){return num1+num2;}//减法运算public int sub(int num1,int num2){return num1-num2;}//乘法运算public int mul(int num1,int num2){return num1*num2;}//除法运算public int div(int num1,int num2){return num1/num2;}}

4.在测试类中测试方法 

@SpringBootApplication
public class Test {public static void main(String[] args) {//获取IOC容器ApplicationContext context=new AnnotationConfigApplicationContext("com.example.demo.aop");//获取容器里的代理类Cal cal=context.getBean(Cal.class);//运行代理类中的方法System.out.println("结果为"+cal.add(5, 4));}
}//结果为:add方法的执行时间为:19
结果为9Process finished with exit code 0

 

2.连接点(Join Point)

和切点类似,满足切点表达式规则的方法, 就是连接点. 也就是可以被AOP控制的方法

简单来说,切点就是定义哪些方法符合要求,而连接点就是满足要求的具体方法

3.通知(Advice)

通知(Advice)是面向切面编程(AOP)中的一个重要概念,它表示在特定的连接点(Join Point)上执行的代码。通知定义了切面的具体行为,例如在方法调用之前执行日志记录、在方法执行之后处理事务提交等。通知与连接点结合使用,可以理解为连接点上执行的具体操作。

特点和分类

  • 与连接点结合:通知与连接点相关联,当特定的连接点被AOP框架拦截时,通知定义的逻辑就会被执行。

  • 多种类型

    • 前置通知(Before Advice):在连接点之前执行,例如验证输入参数。
    • 后置通知(After Returning Advice):在连接点正常执行完毕后执行,例如记录方法返回值。
    • 异常通知(After Throwing Advice):在方法抛出异常后执行,例如处理异常情况。
    • 最终通知(After Finally Advice):无论连接点如何结束,都执行,例如释放资源。
    • 环绕通知(Around Advice):包围连接点的整个执行过程,可以在方法调用前后添加自定义行为,是最强大也是最灵活的一种通知类型。

在上面实现的例子中,通知对应的就是注解@Around,它表示可以对方法执行的前后都可以增强方法

与它同类型的注解举例如下:

@Aspect
@Component
public class LoggingAspect {//@Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();System.out.println("Before executing method: " + methodName);}//@AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")public void logAfterReturning(JoinPoint joinPoint, Object result) {String methodName = joinPoint.getSignature().getName();System.out.println("After returning from method: " + methodName + ", result: " + result);}//@AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {String methodName = joinPoint.getSignature().getName();System.out.println("Exception thrown by method: " + methodName + ", exception: " + ex);}//@After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏@After("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();System.out.println("After executing method: " + methodName);}//@Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏@Around("execution(* com.example.service.*.*(..))")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {String methodName = joinPoint.getSignature().getName();System.out.println("Before around method: " + methodName);Object result = joinPoint.proceed(); // 执行方法System.out.println("After around method: " + methodName + ", result: " + result);return result;}
}

@Before@AfterReturning@AfterThrowing@After@Around 注解分别定义了不同类型的通知,分别对应于前置、后置、异常、最终和环绕通知。

4.切面(Aspect)

切面(Aspect) = 切点(Pointcut) + 通知(Advice)

通过切面就能够描述当前AOP程序需要针对于哪些方法, 在什么时候执行什么样的操作.


四.Spring AOP 的辅助注解

1. @PointCut注解

@Pointcut 是 Spring AOP 中用来定义切点的注解。切点定义了在应用程序中哪些方法需要被织入(应用)切面逻辑。使用 @Pointcut 注解可以将一组方法定义为一个切点,以便在后续的通知(Advice)中引用。

示例如下:

@Pointcut("execution(* com.example.service.*.*(..))")
private void serviceMethods() {}这里的 @Pointcut 注解标注了一个私有方法 serviceMethods(),
它定义了一个切点,用来匹配 com.example.service 包中所有类的所有方法。
切点表达式 "execution(* com.example.service.*.*(..))" 描述了方法的执行

带入我们上面的计算器例子中:

@Component
@Aspect
public class TimeAspect {//定义切点@Pointcut("@annotation(com.example.demo.aop.MyAnnotation)")public void pt(){};//使用切点@Around("pt()")public Object recordTime2(ProceedingJoinPoint pjp) throws Throwable {//记录开始时间long begin=System.currentTimeMillis();//执行目标类的方法Object result=pjp.proceed();//记录结束时间long end=System.currentTimeMillis();//打印结果System.out.println(pjp.getSignature().getName()+"方法的执行时间为:"+(end-begin));//返回原方法的结果return result;}
}

2. @Order注解

在 Spring AOP 中,@Order 注解用于控制切面的优先级顺序。当一个方法被多个切面所通知时,通过 @Order 注解可以指定这些切面的执行顺序,从而控制它们的优先级。

示例:

@Aspect
@Order(1)
public class MyFirstAspect {// Aspect code
}@Aspect
@Order(2)
public class MySecondAspect {// Aspect code
}

@Order注解标识的切面类,执行顺序如下:

• @Before 通知:数字越小先执行

• @After 通知:数字越大先执行


五.Spring AOP 原理

Spring AOP是基于动态代理来实现AOP的

关于动态代理,可以去看看另一篇博客:

常见的设计模式-CSDN博客

Spring AOP 主要使用两种方式来实现动态代理:

1. 基于 JDK 动态代理

  • 基于接口:JDK 动态代理要求目标对象必须实现至少一个接口。Spring 使用 java.lang.reflect.Proxy 类来创建基于接口的代理对象。
  • 工作原理:当应用程序通过接口调用方法时,代理对象会拦截方法调用并执行额外的横切逻辑,然后将控制转交给实际的目标对象。

2. 基于 CGLIB 动态代理

  • 不依赖接口:CGLIB(Code Generation Library)动态代理可以对没有实现接口的类进行代理。它通过继承目标类并重写其中的方法来实现代理功能。
  • 工作原理:CGLIB 在运行时生成目标类的子类,并拦截对父类方法的调用,执行额外的横切逻辑。

关于Spring AOP 的讲解到这里就结束了~

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

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

相关文章

LeetCode 416-分割等和子集

分割等和子集 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例 1&#xff1a; 输入&#xff1a;nums [1,5,11,5] 输出&#xff1a;true 解释&#xff1a;数组可以分割成 [1, 5, 5] 和 [11…

“论SOA在企业集成架构设计中的应用”必过模板,软考高级,系统架构设计师论文

论文真题 企业应用集成(Enterprise Application Integration, EAI)是每个企业都必须要面对的实际问题。面向服务的企业应用集成是一种基于面向服务体系结构(Service-OrientedArchitecture,SOA)的新型企业应用集成技术,强调将企业和组织内部的资源和业务功能暴露为服务,实现…

监督学习:从数据中学习预测模型的艺术与科学

目录 引言 一、监督学习的基本概念 1、数据集 2、特征 3、标签 4、模型 二、监督学习的原理和方法 1、基本原理 2、常用方法 三、监督学习的定义与分类 1、 定义 2.、分类 四、为什么是监督学习&#xff1f; 1、 明确的学习目标 2、高准确率 3、易于评估 4、 …

vue-json-viewer组件 copyable失效,页面并不现实copy按钮

<json-viewer :value"props.row.param_detail.query" :expand-depth"10" copyable> </json-viewer> 官方文档中&#xff0c;说明&#xff0c;只要在json-viewer中加入 copyable属性&#xff0c;即可实现copy功能&#xff0c;如下图&#xff1…

yolov8环境搭建+训练自己数据集

一、yolov8环境搭建 1. 安装miniconda环境 地址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda 选择Python3.8版本 最好安装在C盘 勾选自动添加环境变量 ***以下操作安装过程中关闭代理软件 *** 2. 创建虚拟环境 conda create -n yolov8 python3…

vue自建h5应用,接入企业微信JDK(WECOM-JSSDK),实现跳转添加好友功能

一、项目场景&#xff1a; 1、使用vue开发了一套h5页面的项目 2、这个h5链接是在企业微信里某个地方打开的 3、打开页面的时候有一个好友列表&#xff0c;点击好友列表某一条复制手机号跳转到企业微信添加好友页面 二、实现的效果图 博客只允许上传gif图&#xff0c;所以我只…

浙江工商大学24计算机考研数据,好几个专业都接收调剂,计专复试线284分!

浙江工商大学&#xff08;Zhejiang Gongshang University&#xff09;&#xff0c;简称“浙商大”&#xff08;ZJSU&#xff09;&#xff0c;坐落于浙江省杭州市&#xff0c;是中华人民共和国教育部、中华人民共和国商务部和浙江省人民政府共建的浙江省重点建设高校&#xff0c…

基于YOLO的目标检测系统

探索未来的智能视觉技术 在当今快速发展的科技领域中&#xff0c;基于YOLO&#xff08;You Only Look Once&#xff09;的目标检测系统成为了领先的智能视觉解决方案。这些系统通过深度学习技术实现快速、准确地识别和定位图像或视频中的各种目标&#xff0c;从而提升了安全性…

C#调用OpenCvSharp计算并显示带掩膜的图像直方图

之前的文章简要测试了调用OpenCvSharp的Cv2.CalcHist函数计算直方图的用法&#xff0c;不过使用过程中参数mask的值始终为null&#xff0c;也就是计算的整幅图像的直方图&#xff0c;如果mask不为空&#xff0c;则可以计算图像指定区域的直方图&#xff0c;本文学习掩膜的创建方…

css grid实现九宫格布局

常见的九宫格布局可以使用flex布局实现&#xff0c;但是flex布局有个致命的缺陷&#xff0c;比如3行3列的布局&#xff0c;当第不足3个元素的时候&#xff0c;元素依然是平局平铺的&#xff0c;这样就不满足九宫格的效果&#xff0c;这种情况&#xff0c;使用grid布局可以轻松搞…

Zigbee协议详解:低功耗无线通信的理想选择

什么是Zigbee协议 Zigbee是一种基于IEEE 802.15.4标准的无线通信协议&#xff0c;专为低功耗、低数据速率和短距离通信设计。它广泛应用于物联网&#xff08;IoT&#xff09;设备&#xff0c;如智能家居、工业自动化和健康监测等领域。Zigbee协议由Zigbee联盟维护和推广&#x…

Antd Table 表格 拖拽列宽

antd 的表格组件的列宽&#xff0c;是通过width属性去初始化的&#xff0c;有时候渲染的内容不固定&#xff0c;这个宽做不到通用所以研究怎么实现表格列宽拖动&#xff0c;主要的实现步骤如下&#xff1a; 使用table的components API修改表格头部为 react-resizable提供的组件…

C#事件详解及应用示例

简介 事件是使类具备向其它类通知发生的相关事情的能力。事件被分成两部分&#xff1a;一、引发或发送事件的类&#xff08;称发布者&#xff09;&#xff1b;二、处理或接收事件的类&#xff08;称订阅者&#xff09;。事件也是类型的成员。在 .NET 的桌面应用程序中&#xff…

Netty 入门实例

文章目录 1. 概述2. 代码实例2.1 服务端2.2 客户端2.3 运行截图 3. 整体结构4. 重要组件4.1 EventLoopGroup、EventLoop4.2 Handler & Pipeline4.3 ByteBuf 参考文献 1. 概述 Netty 是一款用于高效开发网络应用的 NIO 网络框架&#xff0c;它大大简化了网络应用的开发过程…

kafka 集群为什么依赖 zookeeper ?

kafka 集群为什么依赖 zookeeper ? 在 Kafka 集群中,ZooKeeper 扮演了关键的角色,负责协调和管理 Kafka 的分布式系统。具体来说,ZooKeeper 在 Kafka 集群中主要起到以下几个作用: 1. 管理集群元数据 ZooKeeper 存储和管理 Kafka 集群的元数据信息,包括 Broker 信息、…

MySQL查询语句语法使用

目录 一、基本查询二、条件查询2.1 简单条件表达式2.2 逻辑表达式2.3 模糊查询 (LIKE)2.4 范围查询 (BETWEEN ... AND ...)2.5 列表查询 (IN)2.6 空值查询 (IS NULL 或 IS NOT NULL) 三、排序查询3.1 基本语法3.2 单列排序3.2 多列排序3.3 使用表达式排序 四、分组查询聚合函数…

mysql的安装和连接

一.数据库相关概 念 1.数据库 存储数据的仓库,数据是有组织的进行存储,简称DB。 2.数据库管理系统 操纵和管理数据库的大型软件,简称DBM。 3.SQL 操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准。简称SQL。 二.市面上流行的数据库 1.ORACLE 2.MySQL …

如何搭建一个成功的短剧制作平台

要搭建一个成功的短剧制作平台&#xff0c;需要考虑多个方面&#xff0c;包括目标定位、技术选择、内容管理、用户体验等。 1、明确目标和定位&#xff1a; 确定你的目标受众是谁&#xff0c;他们的年龄、兴趣、消费习惯等。 明确短剧制作平台的主要定位&#xff0c;是提供原创…

(三)SvelteKit教程:layout 文件

&#xff08;三&#xff09;SvelteKit教程&#xff1a;layout 文件 (1) 设置 layout 文件 我们如果需要添加一些统一的布局文件&#xff0c;我们可以在 routes 文件夹里面添加 layout.svelte 文件&#xff0c;比如我们需要添加 header 和 footer 文件&#xff0c;可以增加 ro…

新能源燃气灶用的是什么燃料?无需燃料,电生明火

新能源燃气灶广义的讲就是用电生明火的烹饪灶具&#xff0c;如&#xff1a;电焰灶、电燃灶或电火灶&#xff0c;无需任何燃料和氧气助燃&#xff1b;而狭义上讲是采用出电能以外的一切新燃料烹饪灶具&#xff0c;如&#xff1a;高功率燃气灶、生物合成油灶等。在厨房革命的浪潮…