1、简介
1.1、AOP 相关概念
Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:
- Target(目标对象):代理的目标对象
- Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
- Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
- Aspect(切面):是切入点和通知(引介)的结合
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
1.2、AOP 开发明确的事项
1. 需要编写的内容
编写核心业务代码(目标类的目标方法)
编写切面类,切面类中有通知(增强功能方法)
在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
2. AOP 技术实现的内容
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的
代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
3. AOP 底层使用哪种代理方式
在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
1.3、知识要点
- aop:面向切面编程
- aop底层实现:基于JDK的动态代理 和 基于Cglib的动态代理
- aop的重点概念:
- Pointcut(切入点):被增强的方法
- Advice(通知/ 增强):封装增强业务逻辑的方法
- Aspect(切面):切点+通知
- Weaving(织入):将切点与通知结合的过程
- 开发明确事项:
- 谁是切点(切点表达式配置)
- 谁是通知(切面类中的增强方法)
- 将切点和通知进行织入配置
2、两种方式
AOP(Aspect-Oriented Programming,面向切面编程)开发可以通过两种主要方式来实现:编程式和声明式。这两种方式分别对应了在代码中直接编写切面逻辑和使用特定的配置来描述切面逻辑的两种不同方式。
- 编程式 AOP: 编程式 AOP 是指在代码中直接编写切面逻辑。这种方式要求开发人员在代码中显式地调用切面逻辑,通常通过方法调用来实现。编程式 AOP 提供了最大的灵活性,开发人员可以精确控制切面逻辑的应用时机,但可能会导致代码冗余和可维护性降低。
- 声明式 AOP: 声明式 AOP 是指使用特定的配置来描述切面逻辑,而不需要在代码中显式调用切面逻辑。这种方式通过在配置文件中或使用注解来定义切面逻辑的应用时机和规则。声明式 AOP 提供了更好的可维护性和代码模块化,使切面逻辑与主要业务逻辑分离,提高了代码的可读性和可维护性。
在声明式 AOP 中,有两种常见的方式:
- 基于 XML 配置: 在配置文件中使用 XML 描述切面逻辑的织入时机和规则,如 Spring AOP 的 XML 配置。
- 基于注解: 使用注解来标记切面逻辑的应用时机和规则,如 Spring AOP 的基于注解的方式。
两种方式各有优势,选择哪种方式取决于项目需求和团队偏好。编程式 AOP 提供了更大的灵活性,适用于一些特定的场景。而声明式 AOP 则提供了更好的可读性和维护性,适用于大多数项目,可以更好地实现横切关注点的分离和处理。
下面着重讲解两种声明式AOP。
3、基于 XML
3.1、快速入门
基本步骤如下:
① 导入 AOP 相关坐标
② 创建目标接口和目标类(内部有切点)
③ 创建切面类(内部有增强方法)
④ 将目标类和切面类的对象创建权交给 spring
⑤ 在 applicationContext.xml 中配置织入关系
⑥ 测试代码
整体目录如下:
3.1.1、导入坐标
<!--导入aop相关坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
3.1.2、创建接口和实现类
3.1.3、创建切面
3.1.4、配置bean
3.1.5、配置织入
3.1.6、测试
3.2、切点表达式
表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
访问修饰符可以省略
返回值类型、包名、类名、方法名可以使用星号* 代表任意
包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
3.2.1、表达式举例
例:
execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
下面依次解释这些切点表达式的含义:
- execution(public void com.itheima.aop.Target.method()):
- 匹配 com.itheima.aop 包下的 Target 类的 method 方法。
- 该方法的修饰符为 public,返回类型为 void,不接受任何参数。
- 表示只匹配具体的类、方法和参数。
- execution(void com.itheima.aop.Target.*(..)):
- 匹配 com.itheima.aop 包下的 Target 类的任何方法。
- 该方法的修饰符不限,返回类型为 void。
- (..) 表示方法可以接受任意个数和类型的参数。
- execution(* com.itheima.aop.*.*(..)):
- 匹配 com.itheima.aop 包下的任何类的任何方法。
- 方法的修饰符、返回类型和参数都不限。
- execution(* com.itheima.aop..*.*(..)):
- 匹配 com.itheima.aop 包及其子包下的任何类的任何方法。
- 方法的修饰符、返回类型和参数都不限。
- execution(* *..*.*(..)):
- 匹配任何包下的任何类的任何方法。
- 方法的修饰符、返回类型和参数都不限。
- 通配符 *..* 表示任意包名和子包。
这些切点表达式用于匹配不同范围的类和方法,可以根据需要来选择最合适的表达式。在 AOP 中,切点表达式用于指定切面逻辑的应用时机,从而实现横切关注点的处理。
3.2.2、表达式抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。
3.3、通知的类型
3.3.1、什么是通知
通知(Advice): 通知是切面的一个具体行为,它决定在连接点处执行何种逻辑。
通常有以下几种类型的通知:前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。
通知(Advice)是 AOP 中的核心概念之一,它代表了在特定连接点(方法调用、对象实例化等)处执行的切面逻辑。通知决定了在连接点何时以及如何执行特定的操作。AOP 提供了不同类型的通知,以便在不同的执行时机应用切面逻辑,从而实现横切关注点的分离。
以下是通常在 AOP 中使用的不同类型的通知:
- 前置通知(Before Advice): 前置通知在目标方法执行之前执行。它通常用于执行一些准备操作,如权限检查、参数验证等。
- 后置通知(After Advice): 后置通知在目标方法执行之后执行,无论目标方法是否抛出异常。它常用于资源释放或记录日志等操作。
- 返回通知(AfterReturning Advice):返回通知在目标方法成功执行并返回结果后执行。它可以获取目标方法的返回值并进行相应处理。
- 异常通知(AfterThrowing Advice): 异常通知在目标方法抛出异常时执行。它可以用于捕获和处理异常,或者执行一些与异常相关的操作。
- 环绕通知(Around Advice):环绕通知是最强大且最灵活的通知类型。它可以完全控制目标方法的执行过程,包括在方法前后进行自定义操作,还可以选择是否调用目标方法。
这些通知类型允许开发人员根据业务需求在不同的执行时机添加切面逻辑。通过组合不同类型的通知,可以实现对应用程序的不同层面进行横切关注点的处理,如日志记录、事务管理、安全性检查等。通知是 AOP 的核心元素之一,它使得切面逻辑能够在合适的时机被织入到目标代码中,从而实现了代码的解耦和模块化。
3.3.2、如何配置
通知类型 |
XML 配置示例 |
前置通知(Before) |
|
后置通知(After) |
|
返回通知(AfterReturning) |
|
异常通知(AfterThrowing) |
|
环绕通知(Around) |
|
4、基于注解
4.1、快速入门
基于注解的aop开发步骤:
① 创建目标接口和目标类(内部有切点)
② 创建切面类(内部有增强方法)
③ 将目标类和切面类的对象创建权交给 spring
④ 在切面类中使用注解配置织入关系
⑤ 在配置文件中开启组件扫描和 AOP 的自动代理
⑥ 测试
目录如下:
4.1.1、创建接口和实现类
4.1.2、创建切面
4.1.3、配置bean
这个时候的切面类,还只是一个普通的Bean,需要后续配置。
4.1.4、配置织入
4.1.5、组件扫描和自动代理
4.1.6、测试
4.2、注解通知类型
通知的配置语法:@通知注解(“切点表达式")
通知类型 |
注解 |
说明 |
前置通知 |
@Before |
指定增强的方法在切入点方法之前执行 |
后置通知 |
@AfterReturning |
指定增强的方法在切入点方法之后执行 |
环绕通知 |
@Around |
指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 |
@AfterThrowing |
指定增强的方法在出现异常的时候执行 |
最终通知 |
@After |
无论增强方法执行是否有异常都会执行 |
4.3、表达式抽取
同 xml 配置 aop 一样,我们可以将切点表达式抽取。
抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后再在增强注解中进行引用。
具体如下: