SpringBoot利用自定义注解实现AOP

本文主要讲解利用SpringBoot的自定义注解来实现AOP思想。

在开始所有的操作之前,需要导入aop坐标:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

如何自定义注解?

实际上注解本质上是一个@interface类,在这个类前面可以通过一些注解来限制这个注解类的使用范围,比如@Target@Retention等等,有关这方面的文章用搜索引擎可以搜出一大把。

1
2
3
4
5
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WriteLog {
String note() default "";
}

自定义注解AOP的切面

切面是有一系列切点与增强组成,所以在@Aspect所注解的类中,需要配置切点增强

切点

使用@Pointcut注解,然并通过@annontationexecution()等方法缺陷切点的范围;
这里因为是使用自定义注解的形式来指定切点,所以是使用@annontation的形式,前提是需要在被增强的方法或者类前面添加自定义的注解。

1
2
3
4
5
6
7
8
/**
* 定义切点
* @param writeLog
*/
@Pointcut("@annotation(writeLog)")
public void writeLogPoincut(WriteLog writeLog) {

}
1
2
3
4
5
6
/**
* 还可以这样定义,但是这样增强就无法获取切点信息
*/
@Pointcut("@annotation(com.songwh.annontation.annontation.WriteLog)")
public void writeLogPoincut() {
}

增强

AOP中的增强主要是以下几种:

  1. Before,在被增强的代码执行之前执行;
  2. After,在被增强的代码执行之后执行
  3. AfterReturning,在被增强的代码正确返回后执行,通过returning可以获取返回值;
  4. Around,在被增强的代码执行前和执行后都会执行,主要是通过ProceedingJoinPoint.process()方法来调用被增强代码;
  5. AgterThrowing,在被增强代码出现异常后执行,可以捕捉到异常的信息,可以通过throwing来获取异常对象。
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@Component
@Aspect
public class WriteLogAspect {

private final static Logger LOGGER = LoggerFactory.getLogger(WriteLogAspect.class);
private final static String POINT_CUT = "writeLogPoincut(writeLog)";


/**
* 定义切点
* @param writeLog
*/
@Pointcut("@annotation(writeLog)")
public void writeLogPoincut(WriteLog writeLog) {

}
/**
* 还可以这样定义,但是这样,增强就无法获取切点信息
*/
// @Pointcut("@annotation(com.songwh.annontation.annontation.WriteLog)")
// public void writeLogPoincut() {
//
// }

/**
* 在主程序出现异常后执行
* @param joinPoint
* @param ex
* @param writeLog
*/
@AfterThrowing(value = POINT_CUT, throwing = "ex")
public void doAfterThrowing(JoinPoint joinPoint, Throwable ex, WriteLog writeLog) {
commonLog(joinPoint, writeLog, "AfterThrowing");
LOGGER.info("出现异常:" + ex.getMessage());
}

/**
* 在主程序执行前执行
* @param joinPoint
* @param writeLog
*/
@Before(value = POINT_CUT)
public void doBefore(JoinPoint joinPoint, WriteLog writeLog) {
commonLog(joinPoint, writeLog, "Before");
}

/**
* 在主程序执行后的增强
* @param joinPoint
* @param writeLog
*/
@After(value = POINT_CUT)
public void doAfter(JoinPoint joinPoint, WriteLog writeLog) {
commonLog(joinPoint, writeLog, "After");
}

/**
* 正确返回后执行的增强
* @param joinPoint
* @param rt
* @param writeLog
*/
@AfterReturning(value = POINT_CUT, returning = "rt")
public void doAfterReturning(JoinPoint joinPoint, Object rt, WriteLog writeLog) {
commonLog(joinPoint, writeLog, "AfterReturning");
LOGGER.info("返回值:" + rt);
}

/**
* 此处必须有返回值,不然是无法
*
* @param proceedingJoinPoint
* @param writeLog
* @return
*/
@Around(value = POINT_CUT)
public Object doAround(ProceedingJoinPoint proceedingJoinPoint, WriteLog writeLog) throws Throwable {
commonLog(proceedingJoinPoint, writeLog, "doAround-before");
Object proceed = null;
// 如果需要使用异常捕获AfterThrowing,此处的异常一改抛出,不能try catch
proceed = proceedingJoinPoint.proceed();
//此处对返回值的修改是有效的
proceed = "around修改后的返回值";

commonLog(proceedingJoinPoint, writeLog, "doAround-after");
return proceed;
}
private void commonLog(JoinPoint joinPoint, WriteLog writeLog, String action) {
Object[] args = joinPoint.getArgs();
LOGGER.info("******************" + action + ":" + writeLog.note() + "******************");
LOGGER.info("方法名:" + joinPoint.getSignature().getName());
LOGGER.info("参数:" + new Gson().toJson(args));
}
}

AOP的运行顺序

在面向切面编程的过程中AOP的执行顺序是:Around-before>>Befer>>主程序执行>>Around-after>>After>>AfterReturning>>End

enter description here

如果是AfterThrowing则则执行顺序是:Around-before>>Befer>>主程序执行>>AfterThrowing>>End

description here

重点

  1. Around增强执行后的返回值一定要返回,否则返回值是null;
  2. 如果既有Around又有AfterThrowing两个增强,那么Around增强里面执行主程序的异常不能进行try catch,否则,AfterThrowing将无法正常捕获异常。
-------------本文结束感谢您的阅读-------------