一文讲清AOP的责任链和递归调用

515 阅读2分钟

一.实验开始前,先写一个切面

@Component
@Aspect
public class ServiceAspect {

    @Pointcut("execution(public * com.netty.use.nettyuse.service.LaService.*(..))")
    public void webLog(){}

    @Around(value = "webLog()")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            System.out.println("Around:方法环绕开始.....");
            Object o =  pjp.proceed();
            System.out.println("Around:方法环绕结束,结果是 :" + o);
            return o;
        } catch (Throwable e) {
            System.out.println(pjp.getSignature() + " 出现异常: "+e.getMessage());
            return "";
        }
    }

    @Before(value = "webLog()")
    public void before(JoinPoint pjp) {
        try {
            System.out.println("before方法执行.....");
        } catch (Throwable e) {
            System.out.println(pjp.getSignature() + " 出现异常: "+e.getMessage());
        }
    }

    @After(value = "webLog()")
    public void after(JoinPoint pjp) {

        System.out.println("after方法执行.....");

    }

    @AfterReturning(value = "webLog()")
    public void afterReturning(JoinPoint pjp) {

        System.out.println("afterReturning方法执行.....");

    }

    @AfterThrowing(value = "webLog()")
    public void afterThrowing(JoinPoint pjp) {
        try {
            System.out.println("afterThrowing方法执行.....");
        } catch (Throwable e) {
            System.out.println(pjp.getSignature() + " 出现异常: "+e.getMessage());
        }
    }

}

该切面中包含了,Around,Before,After,AfterReturning,AfterThrowing类型的通知。

二.责任链中有哪些Advise

我们触发一个切点,debug发现责任链中有如下Advice类,按顺序排列如下:

1.ExposeInvocationInterceptor

2.AspectJAfterThrowingAdvice

3.AfterReturningAdviceInterceptor

4.AspectJAfterAdvice

5.AspectJAroundAdvice

6.MethodBeforeAdviceInterceptor

三.递归调用的过程

责任链的执行核心在ReflectiveMethodInvocation类的proceed方法,如下:

@Override
@Nullable
public Object proceed() throws Throwable {
   // 递归调用的出口
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         return proceed();
      }
   }
   else {
      //调用advise的invoke方法
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

第一个调用的是ExposeInvocationInterceptor的invoke方法

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   MethodInvocation oldInvocation = invocation.get();
   invocation.set(mi);
   try {
      //形成了递归调用
      return mi.proceed();
   }
   finally {
      invocation.set(oldInvocation);
   }
}

入参mi是ReflectiveMethodInvocation类型,ReflectiveMethodInvocation是MethodInvocation的子类。mi.proceed()形成了递归调用。

下面该调用AspectJAfterThrowingAdvice的invoke方法了。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   try {
      //递归调用
      return mi.proceed();
   }
   catch (Throwable ex) {
      if (shouldInvokeOnThrowing(ex)) {
         //异常之后,执行异常处理方法
         invokeAdviceMethod(getJoinPointMatch(), null, ex);
      }
      throw ex;
   }
}

AfterReturningAdviceInterceptor和AspectJAfterAdvice的invoke方法,我们这里就不贴代码了,和AspectJAfterThrowingAdvice的处理类似,都是先递归,递归返回之后,才执行程序员自己定义的通知方法。

我们看一下AspectJAroundAdvice的invoke方法:

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   if (!(mi instanceof ProxyMethodInvocation)) {
      throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
   }
   ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
   ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
   JoinPointMatch jpm = getJoinPointMatch(pmi);
   //执行Around通知方法
   return invokeAdviceMethod(pjp, jpm, null, null);
}

我自己写的Around方法如下:

@Around(value = "webLog()")
public Object around(ProceedingJoinPoint pjp) {
    try {
        System.out.println("Around:方法环绕开始.....");
        //递归调用
        Object o =  pjp.proceed();
        System.out.println("Around:方法环绕结束,结果是 :" + o);
        return o;
    } catch (Throwable e) {
        System.out.println(pjp.getSignature() + " 出现异常: "+e.getMessage());
        return "";
    }
}

它会先执行环绕之前的代码,再进行递归,最后再执行环绕之后的代码。这里执行递归的时候,还剩下MethodBeforeAdviceInterceptor(before通知)还没执行,把before通知执行之后,执行到递归的出口:

if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
   return invokeJoinpoint();
}

因为currentInterceptorIndex是从-1开始的,把所有的Interceptor都调用一次后

currentInterceptorIndex==this.interceptorsAndDynamicMethodMatchers.size() - 1。

这里invokeJoinpoint(),目标方法被执行。