Spring 之 AOP 基础使用

AOP 的常用注解

  • @Before:前置通知,在目标方法执行之前执行
  • @After:后置通知,在目标方法执行之后执行(始终会执行)
  • @AfterReturning:后置返回后通知,在目标方法正常返回后执行(发生异常不会执行)
  • @AfterThrowing:后置异常通知,在目标方法抛出异常后执行
  • @Around: 环绕通知,可以在目标方法执行前后执行自定义逻辑
  • @Pointcut:定义切入点,指定在哪些连接点上应用切面的逻辑
  • @Aspect:定义切面,通常与 @Component 结合使用,将其标记为 Spring 容器中的一个 Bean

Spring 中的 AOP 注解使用

案例代码

  • 引入核心依赖
1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
  • 定义服务类
1
2
3
4
5
6
7
8
9
10
11
@Slf4j
@Service
public class MyService {

public int div(int x, int y) {
int result = x / y;
log.info("result = {}", result);
return result;
}

}
  • 定义切面类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Slf4j
@Aspect
@Component
public class MyAspect {

@Before("execution(public int com.java.interview.spring.aop.MyService.*(..))")
private void beforeNotify() {
log.info("@Before ...");
}

@After("execution(public int com.java.interview.spring.aop.MyService.*(..))")
private void afterNotify() {
log.info("@After ...");
}

@AfterReturning("execution(public int com.java.interview.spring.aop.MyService.*(..))")
private void afterReturningNotify() {
log.info("@AfterReturning ...");
}

@AfterThrowing(pointcut = "execution(public int com.java.interview.spring.aop.MyService.*(..))", throwing = "exception")
private void afterThrowingNotify(Exception exception) {
log.info("@AfterThrowing ...");
log.error("@AfterThrowing exception : {}", exception.getMessage());
}

@Around("execution(public int com.java.interview.spring.aop.MyService.*(..))")
private Object afterAroundNotify(ProceedingJoinPoint joinPoint) {
Object result = null;
log.info("@Around before ...");
try {
// 执行目标方法
result = joinPoint.proceed();
} catch (Throwable throwable) {
log.error("@Around exception : {}", throwable.getMessage());
// 抛出异常,否则不会触发 @AfterThrowing
throw new RuntimeException(throwable);
}
log.info("@Around after ...");
return result;
}

}
  • 定义测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 这里直接基于 SpringBootTest 进行单元测试
*/
@EnableAspectJAutoProxy
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MainTestApplication.class})
public class MainTestApplication {

@Autowired
private MyService myService;

@Test
public void test() {
myService.div(9, 1);
}

}

测试结果

特别注意,Spring 4.x 与 Spring 5.x 的 AOP 通知顺序是不一样的。

Spring 4.x 与 SpringBoot 1.x

注意

这里使用的 Spring 版本是 4.3.13.RELEASE,而且 SpringBoot 的版本是 1.5.9.RELEASE

执行上述代码,输出的结果如下:

1
2
3
4
5
6
2019-05-13 20:48:17.135  INFO 9045 --- [           main] com.java.interview.spring.aop.MyAspect   : @Around before ...
2019-05-13 20:48:17.135 INFO 9045 --- [ main] com.java.interview.spring.aop.MyAspect : @Before ...
2019-05-13 20:48:17.157 INFO 9045 --- [ main] com.java.interview.spring.aop.MyService : result = 9
2019-05-13 20:48:17.159 INFO 9045 --- [ main] com.java.interview.spring.aop.MyAspect : @Around after ...
2019-05-13 20:48:17.160 INFO 9045 --- [ main] com.java.interview.spring.aop.MyAspect : @After ...
2019-05-13 20:48:17.160 INFO 9045 --- [ main] com.java.interview.spring.aop.MyAspect : @AfterReturning ...

此时,如果将上述代码中 myService.div(9, 1) 更改为 myService.div(9, 0),刻意让 Java 代码抛出异常,输出的结果如下:

1
2
3
4
5
6
2019-05-13 20:59:38.560  INFO 12634 --- [           main] com.java.interview.spring.aop.MyAspect   : @Around before ...
2019-05-13 20:59:38.560 INFO 12634 --- [ main] com.java.interview.spring.aop.MyAspect : @Before ...
2019-05-13 20:59:38.578 ERROR 12634 --- [ main] com.java.interview.spring.aop.MyAspect : @Around exception : / by zero
2019-05-13 20:59:38.580 INFO 12634 --- [ main] com.java.interview.spring.aop.MyAspect : @After ...
2019-05-13 20:59:38.580 INFO 12634 --- [ main] com.java.interview.spring.aop.MyAspect : @AfterThrowing ...
2019-05-13 20:59:38.580 ERROR 12634 --- [ main] com.java.interview.spring.aop.MyAspect : @AfterThrowing exception : java.lang.ArithmeticException: / by zero

Spring 4.x 的部分 AOP 通知顺序总结

  • 正常执行:@Before(前置通知)> @After(后置通知)> @AfterReturning(正常返回)
  • 异常执行:@Before(前置通知)> @After(后置通知)> @AfterThrowing(方法异常)

Spring 5.x 与 SpringBoot 2.x

注意

这里使用的 Spring 版本是 5.2.8.RELEASE,而且 SpringBoot 的版本是 2.3.3.RELEASE

执行上述代码,输出的结果如下:

1
2
3
4
5
6
2019-05-13 21:35:33.890  INFO 31351 --- [           main] com.java.interview.spring.aop.MyAspect   : @Around before ...
2019-05-13 21:35:33.891 INFO 31351 --- [ main] com.java.interview.spring.aop.MyAspect : @Before ...
2019-05-13 21:35:33.906 INFO 31351 --- [ main] com.java.interview.spring.aop.MyService : result = 9
2019-05-13 21:35:33.907 INFO 31351 --- [ main] com.java.interview.spring.aop.MyAspect : @AfterReturning ...
2019-05-13 21:35:33.907 INFO 31351 --- [ main] com.java.interview.spring.aop.MyAspect : @After ...
2019-05-13 21:35:33.907 INFO 31351 --- [ main] com.java.interview.spring.aop.MyAspect : @Around after ...

此时,如果将上述代码中 myService.div(9, 1) 更改为 myService.div(9, 0),刻意让 Java 代码抛出异常,输出的结果如下:

1
2
3
4
5
6
2019-05-13 21:36:38.212  INFO 31712 --- [           main] com.java.interview.spring.aop.MyAspect   : @Around before ...
2019-05-13 21:36:38.212 INFO 31712 --- [ main] com.java.interview.spring.aop.MyAspect : @Before ...
2019-05-13 21:36:38.231 INFO 31712 --- [ main] com.java.interview.spring.aop.MyAspect : @AfterThrowing ...
2019-05-13 21:36:38.231 ERROR 31712 --- [ main] com.java.interview.spring.aop.MyAspect : @AfterThrowing exception : / by zero
2019-05-13 21:36:38.233 INFO 31712 --- [ main] com.java.interview.spring.aop.MyAspect : @After ...
2019-05-13 21:36:38.233 ERROR 31712 --- [ main] com.java.interview.spring.aop.MyAspect : @Around exception : / by zero

Spring 5.x 的部分 AOP 通知顺序总结

  • 正常执行:@Before(前置通知)> @AfterReturning(正常返回)> @After(后置通知)
  • 异常执行:@Before(前置通知)> @AfterThrowing(方法异常)> @After(后置通知)