【Spring AOP】【九】Spring AOP源码解析-拦截器/通知器链的执行过程

1  前言

上一节我们说了通知器链的而顺序问题,那么这节我们该看看它的执行了。我们拿JDK代理的执行过程来看哈。

2  源码分析

2.1  方法通读

对于JDK动态代理的,那我们再回来看下当获取到要执行的通知器链后,要做什么呢,看源码:

// 创建方法调用器 并要执行的拦截器传进去
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截器链  也就是说proceed()这个方法里会循环调用或者递归调用
retVal = invocation.proceed();

可以看到创建了ReflectiveMethodInvocation这个对象:

protected ReflectiveMethodInvocation(
            Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
            @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = BridgeMethodResolver.findBridgedMethod(method);
        this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }

那我们重点看下它的proceed()方法:

/**
 * 走到这里我们应该清楚
 * interceptorsAndDynamicMethodMatchers 里边已经有了我们的拦截器并且是按顺序排好的
 */
public Object proceed() throws Throwable {
    /**
     * currentInterceptorIndex起始值是-1
     * 也就是说interceptorsAndDynamicMethodMatchers.size() - 1 表示拦截器都执行完了
     * 该执行目标方法了
     */
    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;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 方法匹配判断 匹配到就执行拦截器的invoke方法 并把this传过去了 也就是执行器链 这样那边就可以继续proceed()执行下一个拦截器咯
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 匹配失败 继续proceed() 到下一个
            return proceed();
        }
    }
    else {
        // 执行拦截器逻辑,并传递 this
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

大致流程就是:从第一个通知器开始执行,直到最后一个通知器然后执行目标方法,那我们接下来看看这5类通知器,我按照通知器类型的顺序来哈。

2.2  @Around环绕通知-AspectJAroundAdvice

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);
    // 执行环绕通知的增强
    return invokeAdviceMethod(pjp, jpm, null, null);
}

顺便我们拿来我们例子:

@Around("myPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("around before");
    Object res = joinPoint.proceed();
    System.out.println("around after");
    return res;
}

这时候我们是不是控制台会打印出:around before

执行过过程图:

看我们的例子接下来执行joinPoint.proceed(); 也就是要执行我们实际的目标方法,joinPoint其实还是我们的通知器链,那我们proceed()是不是继续执行下一个通知器么?是不是就到了前置通知了。

2.3  @Before前置通知-MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
   invokeAdviceMethod(getJoinPointMatch(), null, null);
}

执行前置通知,然后继续proceed()继续下一个,这里我有三个前置通知器,所以跳过两个通知器哈,调到该我们的后置通知了。

2.4  @After后置通知-AspectJAfterAdvice

public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            // 继续下一个通知器
            return mi.proceed();
        }
        finally {
            // 调用通知方法
            invokeAdviceMethod(getJoinPointMatch(), null, null);
        }
    }
@After("myPointCut()")
    public void myAfter(JoinPoint point) {
        System.out.println("myAfter");
    }

可以看到finally里才会执行后置通知,也就是继续下一个通知器即返回通知,当通知器都执行完以及目标方法执行完时才会执行finally即后置通知,那我问问你后置通知执行完代码回到哪里了?

2.5  @AfterReturning返回通知-AfterReturningAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}

看返回通知,先执行下一个通知器即异常通知,当通知器都执行完以及目标方法执行完时才会执行finally即后置通知,那我问问你后置通知执行完代码又回到哪里了?

2.6  @AfterThrowing异常通知-AspectJAfterThrowingAdvice

public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}

异常通知,看也是执行下一个通知器,已经到底了,所以执行目标方法。到这里了,问问你自己,下一步该执行什么了?是不是到返回通知了。

继续后置通知以及环绕通知结束。

 ok到这里,我们的代理的一个执行过程就结束了,结合成代码可以看成这样:

// 环绕通知前
// 前置通知
try {
    // 返回通知
    try {
        // 目标方法
    } catch (Throwable var3) {
        // 异常通知
    }
} finally {
    // 后置通知
}
// 环绕通知后

3  小结

好了,到这里我们的代理执行过程也看的差不多了,整个AOP整体上的一个过程,也有了大致的理解,哪里有理解不对的地方欢迎指正哈,大家也可以试试多个Aspect的时候,通知器的顺序是怎么样的,执行的过程什么样的,加油。

posted @ 2023-02-22 23:28  酷酷-  阅读(119)  评论(0编辑  收藏  举报