基本概念

在 spring 中 Aspect 通过@Aspect@Pointcut和一系列 advice(@Before@after@afterReturning@Around)组成。

  • @Aspect 切面,声明当前 class 为一个切面
  • @Pointcut 切入点,通过 execution 表达式描述切入规则,
  • advice 通知,定义了切面是什么以及何时使用。
    advice 中还包含了 连接点(joinpoint)
  • weave 织入,将切面应用到目标对象并导致代理对象创建的过程
  • introduction 引入,在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

advice 类型

@Before

前置通知(Before advice):在某连接点(JoinPoint)——被切入代码(类或者方法)之前执行的通知,但这个通知不能阻止连接点前的执行。因为@Before 注解的方法入参不能传 ProceedingJoinPoint,而只能传入 JoinPoint。而从 aop 走到核心代码就是通过调用 ProceedingJionPoint 的 proceed()方法。而 JoinPoint 没有这个方法。
这里牵扯区别这两个类:Proceedingjoinpoint 继承了 JoinPoint 。是在 JoinPoint 的基础上暴露出 proceed 这个方法。proceed 很重要,这个是 aop 代理链执行的方法。暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到 JoinPoint,这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。

@After

后通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

@AfterReturning

返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。

@Around

环绕通知(Around advice):包围一个连接点的通知,类似 Web 中 Servlet 规范中的 Filter 的 doFilter 方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。这时 aop 的最重要的,最常用的注解。用这个注解的方法入参传的是 ProceedingJionPoint pjp,可以决定当前线程能否进入核心方法中——通过调用 pjp.proceed();

@AfterThrowing

抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。

advice 注解的执行先后顺序

基础顺序

执行到核心业务方法或者类时,会先执行 AOP。在 aop 的逻辑内,先走@Around 注解的方法。然后是@Before 注解的方法,然后这两个都通过了,走核心代码,核心代码走完,无论核心有没有返回值,都会走@After 方法。然后如果程序无异常,正常返回就走@AfterReturn,有异常就走@AfterThrowing

多个切面顺序

注意点

  • 如果在同一个 aspect 类中,针对同一个 pointcut,定义了两个相同的 advice(比如,定义了两个 @Before),那么这两个 advice 的执行顺序是无法确定的,即使在这两个 advice 添加了 @Order 这个注解,也不行。
  • 对于@Around 这个 advice,不管它有没有返回值,但是必须要方法内部,调用一下 pjp.proceed();否则方法将不会会被执行,从而也导致了 @Before 这个 advice 不会被触发。