Spring AOP 机制总结
寄语:刚开始学aop的时候是大三吧,老师讲的不好,我也没学好,导致现在才有个较为清晰的认知,那个时候只知道有aop,
根部不明白aop的作用,时至今日,任然觉得aop难以咀嚼,奈何平时不用面试要用,特此总结。
下面开始总结:
注意:我使用的是springboot2.1+maven3.6,请自行搭建基础环境
项目github地址:https://github.com/zgq7/devloper-mine/blob/master/src/main/java/com/dev/config/aop/BaseAop.java
1:导入maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.1.6.RELEASE</version> </dependency>
2:切面类
package com.dev.config.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import java.util.Collections; /** * Created by zgq7 on 2019/7/12.
* * 各通知执行顺序: * 1:around-> * 2:before-> * 3:around->retrun code -> * 4:after-> * 5:afterReturning-> * 6:afterThrowing * </p> * <p> * order 执行顺序: * 1:进入目的方法时:order值越小越先执行 * 2:从目的方法出去时:order值越大越先执行 * </p> */ @Aspect @Order(1) public class BaseAop { private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * 写入具体切面点 * * @apiNote execution 路径中的 "." 如果是精确到类,则一个就行,如果是精确到包就得两个 "." * @apiNote execution 路径若只精确到包,就无法使用 before、after、afterReturuning、around 四个通知模块,启动会报错 **/ @Pointcut("execution(public * com.dev.controller.TestController.s())") public void aspect() { } /** * 进入方法体前 **/ @Before(value = "aspect()") public void before(JoinPoint joinPoint) { log.info("before :参数类型:{}", joinPoint.getArgs()); } /** * 该切面返回数据前(在retrun之前执行) **/ @After(value = "aspect()") public void after(JoinPoint joinPoint) { log.info("aop before return :{}", joinPoint.toLongString()); } /** * 该切面返回数据后 * joinPoint.getSignature() 返回方法放回类型 及 方法路径 **/ @AfterReturning(value = "aspect()", returning = "result") public void afterReturning(Object result) { log.info("aop after return :返回结果:{}", result); } /** * 环绕通知 * 1:before之前 * 2:afterReturning之后 * * @apiNote 1:@Around 下接的方法的参数必须为 ProceedingJoinPoint 类型, * @apiNote 2:proceedingJoinPoint.proceed() 产生的结果即为 @AfterReturning 中的 result,可 debug 调试 * @apiNote 3:由于@Around要提前获取到目的方法的执行结果,且@Around提前于@AfterThrowing运行,因此异常将在@Around中被捕获,从而导致@AfterThrowing捕获不到异常,因此@Around与@AfterThrowing混合使用 **/ //@Around(value = "aspect()") public Object around(ProceedingJoinPoint proceedingJoinPoint) { log.info("aop arrounding :{}", proceedingJoinPoint.getSignature().getName()); try { return proceedingJoinPoint.proceed(); } catch (Throwable throwable) { log.info("aop arrounding error :{}", throwable.getMessage()); //throwable.printStackTrace(); return Collections.EMPTY_MAP; } } /** * 切面报错 **/ @AfterThrowing(value = "aspect()", throwing = "exception") public void afterThrowing(Throwable exception) { log.error("exception occured , msg : {}", exception.getMessage()); if (exception instanceof NullPointerException) log.info("空指针异常"); } }
2.1:多个切面的执行顺序
1:进入目的方法时:order值越小越先执行
2:从目的方法出去时:order值越大越先执行
notice2.2:aspect 中 @befer、@after、@afterReturning、@around、@afterThrowing的执行顺序
* 1:around-> * 2:before-> * 3:around->retrun code -> * 4:after-> * 5:afterReturning
notice2.2.1:各项通知也严格按照order的值进行传递一层一层的走下去
notice2.3:aspect 中@around 下属的方法必须要有返回值,否则就是一个 “有问题” 的切面
* @apiNote 1:@Around 下接的方法的参数必须为 ProceedingJoinPoint 类型, * @apiNote 2:proceedingJoinPoint.proceed() 产生的结果即为 @AfterReturning 中的 result,可 debug 调试
notice2.4:aspect 中@around 不能与 @afterThrowing 混用
* @apiNote 3:由于@Around要提前获取到目的方法的执行结果,且@Around提前于@AfterThrowing运行,因此异常将在@Around中被捕获,从而导致@AfterThrowing捕获不到异常,因此@Around与@AfterThrowing混合使用
notice2.5:aspect 中@PointCut中的execution类路径或者包路径问题
* @apiNote execution 路径中的 "." 如果是精确到类,则一个就行,如果是精确到包就得两个 "." * @apiNote execution 路径若只精确到包,就无法使用 before、after、afterReturuning、around 四个通知模块,启动会报错