AOP 是什么
AOP 的中文和英文全称分别是:面向切面编程 和 Aspect Oriented Programming 。
它是将复杂重复的无关业务的逻辑从代码中“切出来”进行统一封装,进行代码的解耦。极大地提高了业务代码的清晰度,使项目更有调理,代码更强壮。
AOP 术语 & 增强类型
AOP 术语
- 切面(
Aspect) :一般单独作为一个类,他是Pointcut和Jointpoint的集合,在AOP中表示为在哪干和干什么集合。 - 连接点(
Jointpoint) :方法中可以插入 AOP 的一点。即使用 Spring AOP 框架采取操作的实际位置。 - 切入点(
Pointcut) :多个Jointpoint的集合,表示在哪里执行Advice增强代码。 - 增强/通知(
Advice) :具体的增强的代码操作,表示做了什么。 - 引入(
inter-type declaration) :引用可以让我们向类添加新的字段和方法 - 织入(
Weaving) :创建一个被增强对象,这些可以在编译时或类加载时和运行时完成
AOP 增强类型
- 前置增强(
Before advice) :在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。 - 后置增强(
After returning advice) :在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。 - 异常增强(
After throwing advice) :在方法抛出异常退出时执行的通知。 - 最终增强(
After (finally) advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 - 环绕增强(
Around Advice):环绕通知可以在方法调用前后完成自定义的行为。
使用方式
1. AspectJ 注解配置方式
为了使用这个注解,有两种配置的方式,分别为 xml 和 AspectJ 。
(1)XML 配置方式 :
xml 配置已经过时:
<aop:aspectj-autoproxy/>
(2)注解 @EnableAspectJAutoProxy 配置方式 :
注意:@Aspect 注解的类,必须被 Spring 容器扫描到(即必须是 Bean),否则 AOP 不生效。
@Configuration //配置类注解
@EnableAspectJAutoProxy //AspectJ配置注解
public class Config {
}
(3)配置完后 AspectJ 的使用方式 :
在使用前首先要注意的是:
- 配置完毕后,所有写了
@Aspect的Bean都会被 Spring 当作成一个Aspect切面。 - 另外只有被 IoC 接管的 Bean (三种IoC的配置方式,xml 配置的,javaConfig 配置的,注解配置的)才能进行
@Aspect注解,所以要用@Component等注解将其先修饰成一个 Bean 。 或者xml配置和javaConfig配置的一系列操作。
例如如下代码:
// 有效的AOP配置类
@Aspect //(定义切面)
@Component //(将其定义为 Bean 交给 SpringIoC 管理)
public class MyAspect {
//....
}
2. Pointcut 配置
写在Aspect配置类中
(1) execution
@Pointcut("execution(* testExecution(..))")
public void anyTestMethod()
{
//...
}
- 这是 AspectJ 的核心,也是 Spring AOP 最常用的方式。
- 它通过方法签名(返回值、包路径、类名、方法名、参数)来匹配。
(2) within
@Pointcut("within(ric.study.demo.aop.svc..*)")
public void inSvcLayer()
{
//...
}
- 它只关心类在哪个包下,不关心方法叫什么。一行代码就能覆盖整个模块。
(3) @annotation
@Pointcut("@annotation(ric.study.demo.aop.HaveAop)")
public void withAnnotation()
{
//...
}
- 它可以进行定义注解,之后在使用的过程中出现上面代码中的“
@HaveAop”时,就会进行拦截,执行这个方法对应的增强 Advice 。 - 它匹配带有特定注解的方法。这是目前业务开发中最推荐的方式。
(4) Bean
@Pointcut("bean(testController)")
public void inControllerLayer()
{
//...
}
- 只对某个特定的 Bean 做特殊的切面增强
四个方式的总结
- 首选
@annotation:
对于具体的业务功能,这是最好的选择。虽然需要加注解,但它让代码的可读性和维护性达到了最高,一看代码就知道这个方法被 AOP 增强了。“颗粒度”更细,execution通常匹配一大类方法(比如所有 service 包),而@annotation可以精确到某一个具体的方法。 - 次选
execution+within:
对于非业务逻辑的通用技术组件,使用execution或within匹配包路径。这样我们就不需要在代码里到处加注解,可以保持代码整洁。 - 尽量少用
bean:
3. 配置 Advice 增强
首先要注意的是:Aspect 类应该遵守单一职责原则,不要把所有的Advice配置全部写在一个Aspect类里面。
(1)@Before 前置增强
@Before("servicePointcut()")
public void doBeforeAdvice() {
System.out.println("[前置增强] 目标方法即将执行");
}
}
- 在执行方法前运行
(2)@AfterReturning 后置增强
@AfterReturning(pointcut = "servicePointcut()", returning = "result")
public void doAfterReturningAdvice(Object result) {
System.out.println("[后置增强] 方法执行成功,返回值: " + result);
}
- 在执行方法成功后运行(无异常的情况下,有异常不执行)
(3)@AfterThrowing 异常增强
@AfterThrowing(pointcut = "servicePointcut()", throwing = "ex")
public void doAfterThrowingAdvice(Exception ex) {
System.out.println("[异常增强] 方法抛出异常: " + ex.getMessage());
}
- 在方法出现异常时运行
(4)@After 最终增强
@After("servicePointcut()")
public void doAfterAdvice() {
System.out.println("[最终增强] 方法执行结束(无论成功或异常)");
}
- 无论是否异常,在方法执行结束后运行
(5)@Around 环绕增强
@Around("servicePointcut()")
public Object doAroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[环绕增强] 方法执行前");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("[环绕增强] 方法执行后");
return result;
}
关于这五个 Advice 增强方法的执行顺序详解:
情况一:正常执行流程 (无异常):
- @Around (环绕前)
内置的环绕增强的代码会首先执行,直到遇到joinPoint.proceed()这一行,让流程进入下一环节。 - @Before (前置增强)
一旦proceed()被调用,流程就进入了前置增强。通常进行方法的准备工作 - 目标方法执行
前置增强执行结束之后,真正的业务逻辑(比如 Service 方法)开始执行。 - @AfterReturning (返回增强)
如果目标方法成功执行并返回了结果,流程就会触发返回后增强。这是处理返回值、记录成功日志的地方。因为如果方法抛出了异常,这一步会被跳过。 - @Around (环绕后)
在@AfterReturning之后,流程会回到proceed()方法调用的下一行。环绕增强可以继续处理返回值,或者计算方法的执行耗时。 - @After (最终增强)
这是整个流程的“收尾”工作。无论前面目标方法执行成功还是失败,最终增强都一定会执行。常用于释放资源。
情况二:执行流程出现异常(抛出异常):
- @Around (环绕前)
流程同样从环绕增强开始。 - @Before (前置增强)
调用proceed()后,执行前置增强。 - 目标方法
业务逻辑开始执行,但里面的方法在执行时抛出了一个异常! - @AfterThrowing (异常增强)
一旦目标方法抛出异常,流程会立即中断,并跳转到异常增强。这里是记录错误日志、发送告警通知的位置。此时@AfterReturning和 "@Around的proceed()" 后的方法不执行(因为抛出异常了,方法执行失败)。 - @After (最终增强)
和正常流程一样,无论是否发生异常,最终增强都一定会执行,用于清理资源。
流程表格:
| 增强类型 | 正常流程 | 异常流程 | 核心特点 |
|---|---|---|---|
| @Around (proceed前) | 执行 | 执行 | 功能最强,可控制方法执行 |
| @Before | 执行 | 执行 | 在目标方法之前,做准备工作 |
| 目标方法执行 | 无异常 | 出现异常! | |
| @AfterReturning | 执行 | 不执行 | 仅在方法成功返回后执行 |
| @AfterThrowing | 不执行 | 执行 | 仅在方法抛出异常后执行 |
| @Around (proceed后) | 执行 | 不执行 | 功能最强,可控制方法执行 |
| @After | 执行 | 执行 | 无论成败,最终都会执行 |
通过对比可以看出:
@AfterReturning和@Around(中的后置方法) 是同时出现(无异常)。- 而
@AfterThrowing是和他们对立出现(有异常)。
结语
本文结束
如果这篇文章帮你理清了思路,我也感到很开心。
这也同时是我的学习笔记,能帮到别人我不胜感谢,若哪里出错希望指出,同样不胜感激!
以上,@Aroaku