AOP在Spring项目中相当常见,能够无侵入的实现不少功能:
在AOP中有好几个方法,分别为:@Around
、@Before
、@After
、@AfterReturing
、@AfterThrowing
该文主要探讨3个问题:
单个切面中核心方法执行顺序?
如何控制多个切面的执行顺序?
多个切面中方法的执行顺序?
代码可以很好的验证上述问题,有代码如下:
package com.mc.aopstudy.aspect;
import cn.hutool.core.convert.Convert;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 所有请求的切面
*
* @author LiuChunfu
* @date 2018/2/7
*/
@Aspect
@Component
public class RequestAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void pointCut() {
}
/**
* 当定义一个Around增强处理方法时,该方法的第一个形参必须是ProceedJoinPoint类型(至少含有一个形参),在增强处理方法体内,调用ProceedingJoinPoint参数的procedd()方法才会执行目标方法——这就是Around增强处理可以完全控制方法的执行时机、如何执行的关键;如果程序没有调用ProceedingJoinPoint参数的proceed()方法,则目标方法不会被执行。下面定义一个Around增强处理。
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("R1-around-start");
//此处必须将结果获取后返回才会正常对代码进行执行
Object obj = joinPoint.proceed();
System.out.println("R1-around-end");
return Convert.toStr(obj) + "123";
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("R1-before");
}
/**
* After增强处理不管目标方法如何结束(包括成功完成和遇到异常中止两种情况),它都会被织入。
*/
@After("pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("R1-after");
}
@AfterReturning("pointCut()")
public void afterReturing(JoinPoint point) {
System.out.println("R1-afterReturing");
//return returnValue;
}
/**
* 怎么获取是哪一类异常?
* @param joinPoint
*/
@AfterThrowing("pointCut()ut()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("R1-afterThrowing");
}
}
执行结果:
R1-around-start
R1-before
method invoke
R1-around-end
R1-after
R1-afterReturing
其中 method invoke 是 调用的被切的方法的输出结果。
经过分析很容易分析出:
Around Start → Start → Invoke → Around End → After → After Returing
可以分为如下规则记忆:
如果我有多个切面,如何控制这几个切面执行的先后顺序了?
有如下2种比较好方法:
@Order(100)
@Aspect
@Component
public class RequestAspect {}
上述代码种新增了一个注解是 @Order(value)
其中value的值越小,执行顺序越靠前。
该方式最简单,推荐!
import org.springframework.core.Ordered;
@Aspect
@Component
public class RequestAspect implements Ordered {
@Override
public int getOrder() {
return 0;
}
}
实现接口org.springframework.core.Ordered
种的getOrder()
方法。也是value值越小,执行顺序越靠前。
对于同一个方法,可能会有多个切面对其进行处理,那么其顺序如何了?
还是通过代码进行处理,下面有2部分代码:
切面1的代码:
package com.mc.aopstudy.aspect;
import cn.hutool.core.convert.Convert;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class RequestAspect1 {
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("R1-around-start");
Object obj = joinPoint.proceed();
System.out.println("R1-around-end");
return Convert.toStr(obj) + "123";
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("R1-before");
}
@After("pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("R1-after");
}
@AfterReturning("pointCut()")
public void afterReturing(JoinPoint point) {
System.out.println("R1-afterReturing");
//return returnValue;
}
@AfterThrowing("pointCut()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("R1-afterThrowing");
}
}
切面2的代码:
package com.mc.aopstudy.aspect;
import cn.hutool.core.convert.Convert;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(100)
public class RequestAspect2 {
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("R2-around-start");
Object obj = joinPoint.proceed();
System.out.println("R2-around-end");
return Convert.toStr(obj) + "123";
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
System.out.println("R2-before");
}
@After("pointCut()")
public void after(JoinPoint joinPoint) {
System.out.println("R2-after");
}
@AfterReturning("pointCut()")
public void afterReturing(JoinPoint point) {
System.out.println("R2-afterReturing");
}
@AfterThrowing("pointCut()")
public void afterThrowing(JoinPoint joinPoint) {
System.out.println("R2-afterThrowing");
}
}
执行结果是什么了?
R1-around-start
R1-before
R2-around-start
R2-before
method invoke
R2-around-end
R2-after
R2-afterReturing
R1-around-end
R1-after
R1-afterReturing
从结果来分析:
Around
的时候把R2当成1个整体进行,当R2执行完毕后,才会执行R1后续的。用一个图对此进行表示如下: