SpringBoot中AOP编程
一、AOP是什么
AOP:Aspect Oriented Programming,翻译过来就是“面相切面编程”,它是对面相对象的一种补充和完善。
AOP的使用场景:
根据其使用场景我能知道起始AOP的实现很像我们现实中需要对业务代码部分进行切入业务实现逻辑。
它有以下特点:
- 侵入性小,几乎可以不改动原来逻辑的情况下把新的逻辑加入业务。
- 实现方便,使用几个注解就可以实现,使系统更容易拓展。
- 更好的复用代码,比如事务日志打印,简单逻辑适合所有情况。
二、AOP中注解的含义
@Aspect
:切面。表示一个横切进业务的一个对象。他里面包含切入点(Pointcut)和Advice(通知)。
@Pointcut
:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。
@Before
:Advice(通知)的一种,切入点的方法体执行之前执行。
@Around
:Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。
@After
:Advice(通知)的一种,在切入点正常运行结束后执行。
@AfterReturning
:Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行
@AfterThrowing
:Advice(通知)的一种,在切入点运行异常时执行。
三、Pointcut切入点的语法
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
|
@Pointcut("within(com.leo.controller..*)") public void pointcutWithin(){
}
@Pointcut("this(com.leo.controller.HelloController)") public void pointcutThis(){
}
@Pointcut("target(com.leo.service.UserInfoService)") public void pointcutTarge(){
}
@Pointcut("bean(*ServiceImpl)") public void pointcutBean(){
}
@Pointcut("args(String, ..)") public void pointcutArgs(){
}
@Pointcut("@annotation(org.springframework.stereotype.Controller)") public void pointcutAnnocation(){
}
@Pointcut("@within(org.springframework.stereotype.Controller)") public void pointcutWithinAnno(){
}
@Pointcut("@target(org.springframework.stereotype.Controller)") public void pointcutTargetAnno(){
}
@Pointcut("@args(org.springframework.stereotype.Service)") public void pointcutArgsAnno(){
}
@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))") public void pointCut() {
}
|
需要注意:上面的匹配的类型中支持或(||)与(&&)非(!)运算。
四、AOP代码实现
首先配置pom.xml
进行注解jar包的支持
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
|
然后在配置文件中启动注解支持,例如我的是myspringmvc-servlet.xml
文件,这里需要注意的是注解要跟配置文件在一块,举个例子:如果需要切入Controller
的配置就需要在myspringmvc-servlet.xml
中添加,如果是切入应用的配置如Service
则需要在applicationContext.xml
中添加。
1 2 3
| <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
|
AOP核心代码如下:
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
| ThreadLocal<Long> startTime = new ThreadLocal<>();
@Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))") public void pointCut() {
}
@Before(value = "pointCut()") public void beforeLog(JoinPoint joinPoint) { System.out.println("进入LogAop的beforeLogger"); startTime.set(System.currentTimeMillis()); }
@Around(value = "pointCut()") public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入LogAop的aroundLogger"); return pjp.proceed(); }
@After(value = "pointCut()") public void afterLog() { System.out.println("进入LogAop的afterLogger"); long start = startTime.get(); System.out.println("方法体执行耗时:" + (System.currentTimeMillis() - start) + "ms"); startTime.remove(); }
@AfterReturning(value = "pointCut()",returning = "o") public void afterRunningLog(Object o){ System.out.println("进入LogAop的afterRunningLog"); System.out.println(o.getClass()); }
@AfterThrowing(value = "pointCut()") public void afterThrowingLog(){ System.out.println("进入LogAop的afterThrowingLog"); }
|
执行顺序:

- 执行完
pjp.proceed()
之后,开始进入@Before方法





所以,正常的执行顺序是:@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterReturning
如果存下异常那执行顺序是什么呢?以下模拟了一个异常。



因此如果异常在Around中pjp.proceed()之前,则执行顺序为:@Around -> @After -> @AfterThrowing
同理,如果异常在Around中pjp.proceed()之后,则执行顺序为:@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterThrowing