likes
comments
collection
share

详细分析Spring的AOP源码下篇

作者站长头像
站长
· 阅读数 12

前言

本篇文章是SpringAOP的源码学习分享,分为上下两篇,在详细分析Spring的AOP源码上篇中已知SpringAOP的切面类织入业务bean后,会为业务bean生成动态代理对象,这个动态代理对象中持有需要生效的所有通知,叫做通知链。

本篇将对调用AOP动态代理对象时的整个流程进行学习,以探究切面中的类似于前置通知或者后置通知这种通知方法是如何对目标bean的目标方法进行增强的。

注:本文均基于JDK动态代理。

正文

一. AOP动态代理对象结构分析

在上篇的示例工程中,可以看一下测试程序中从容器获取到的IMyServicebean是什么样子,调试图如下所示。

详细分析Spring的AOP源码下篇

可以看到获取出来的bean实际为MyServiceJDK动态代理对象,InvocationHandlerJdkDynamicAopProxyJdkDynamicAopProxy中持有ProxyFactoryProxyFactory中持有目标对象和通知链。

二. AOP动态代理对象调用分析

调用动态代理对象的方法时,会调用到InvocationHandlerinvoke() 方法,这里InvocationHandlerJdkDynamicAopProxy,所以将JdkDynamicAopProxyinvoke() 方法作为入口开始分析。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 不会将通知作用在equals()方法,除非目标对象实现的接口中定义了equals()方法
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 不会将通知作用在hashCode()方法,除非目标对象实现的接口中定义了hashCode()方法
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 不会将通知作用于Advised接口或者其父接口中定义的方法
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // 获取目标对象
        target = targetSource.getTarget();
        // 获取目标对象的Class对象
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 将通知链中能够作用于当前方法的通知组装成拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            // 如果拦截器链是空,那么直接调用目标对象方法
            // AopUtils.invokeJoinpointUsingReflection()最终会调用到method.invoke(target, args)
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 创建方法调用器MethodInvocation,实际为ReflectiveMethodInvocation
            // 创建ReflectiveMethodInvocation时传入的参数依次为:代理对象,目标对象,目标方法,目标方法参数,目标对象的Class对象,拦截器链
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 调用方法调用器的proceed()方法,开始进入调用各个通知方法和目标方法的递归流程
            retVal = invocation.proceed();
        }

        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

上述的invoke() 方法主要是做了两件事情:

  • 第一件事情是将通知链中所有能够作用于当前目标方法的通知构建成拦截器链,并基于拦截器链生成方法调用器ReflectiveMethodInvocation
  • 第二件事情就是调用ReflectiveMethodInvocationproceed() 方法,这个方法会调用到目标方法,并且在调用的过程中,拦截器链中的拦截器也会执行以达到增强功能的效果。

下面先看一下通知链如何构建成拦截器链,this.advised实际就是生成动态代理对象的时候的ProxyFactory,而ProxyFactory继承于AdvisedSupport,将通知链构建成拦截器链的方法就是AdvisedSupportgetInterceptorsAndDynamicInterceptionAdvice(),如下所示。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    // 先根据Method从缓存中拿拦截器链,缓存中没有时会去生成并缓存起来
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        // 实际调用到DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice()方法来生成拦截器链
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}

每个目标方法对应的拦截器链在生成后都会被缓存,所以会先从缓存中拿拦截器链,缓存中没有时会去调用DefaultAdvisorChainFactorygetInterceptorsAndDynamicInterceptionAdvice() 方法来生成拦截器链,getInterceptorsAndDynamicInterceptionAdvice() 方法实现如下。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
        Advised config, Method method, @Nullable Class<?> targetClass) {

    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    // 先从ProxyFactory中将通知链获取出来
    Advisor[] advisors = config.getAdvisors();
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;

    for (Advisor advisor : advisors) {
        // 因为Advisor接口有两个子类接口,分别是PointcutAdvisor和IntroductionAdvisor
        if (advisor instanceof PointcutAdvisor) {
            // 处理PointcutAdvisor,示例中使用的都是PointcutAdvisor
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 获取切点对象,这里的mm类型为AspectJExpressionPointcut
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                boolean match;
                if (mm instanceof IntroductionAwareMethodMatcher) {
                    if (hasIntroductions == null) {
                        hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                    }
                    // 判断当前Advisor是否能够作用于目标方法
                    match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                }
                else {
                    // 判断当前Advisor是否能够作用于目标方法
                    match = mm.matches(method, actualClass);
                }
                if (match) {
                    // 如果当前Advisor能够作用于目标方法,那么将当前Advisor转换为MethodInterceptor,即通知转换为方法拦截器
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    if (mm.isRuntime()) {
                        for (MethodInterceptor interceptor : interceptors) {
                            // 将方法拦截器和切点对象封装成InterceptorAndDynamicMethodMatcher并加入拦截器链中
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }
        else if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
        else {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
        }
    }

    return interceptorList;
}

通知链转换为拦截器链的过程概括如下。

  • 先从Advisor中将切点对象获取出来,并根据切点对象判断当前Advisor是否能够作用于目标方法;
  • 将能够作用于目标方法的Advisor封装成方法拦截器MethodInterceptor,并加入拦截器链。

MethodInterceptor是一个接口,定义了方法拦截器,示例中使用的前置通知和后置通知对应的方法拦截器分别为MethodBeforeAdviceInterceptorAspectJAfterAdvice,它们的关系可以用下面的类图表示。

详细分析Spring的AOP源码下篇

拦截器链获取到后,在JdkDynamicAopProxyinvoke() 方法中还会再创建一个方法调用器ReflectiveMethodInvocation,其类图如下所示。

详细分析Spring的AOP源码下篇

通过类图可以知道,ReflectiveMethodInvocation中持有代理对象,目标对象,目标方法,目标方法参数,目标对象的Class对象,拦截器链,后续调用通知方法以及调用目标方法的逻辑的入口,就是ReflectiveMethodInvocationproceed() 方法。

上面分析了AOP动态代理对象调用时,JdkDynamicAopProxyinvoke() 方法中做的第一件事情,即将通知链中所有能够作用于当前目标方法的通知构建成拦截器链,并基于拦截器链生成方法调用器ReflectiveMethodInvocation。下面分析第二件事情,即调用ReflectiveMethodInvocationproceed() 方法,通过调用proceed() 方法可以在调用目标方法的前后将通知的增强应用到目标方法上,下面分析一下整个调用流程,ReflectiveMethodInvocationproceed() 方法实现如下。

// currentInterceptorIndex用于指示当前需要调用的拦截器
// 初始为-1,每次使用前会先加1
private int currentInterceptorIndex = -1;

public Object proceed() throws Throwable {
    // 当拦截器都遍历完后,则调用目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 调用invokeJoinpoint()方法来执行目标方法
        // invokeJoinpoint()会调用AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments),即通过反射执行目标方法
        return invokeJoinpoint();
    }

    // 把拦截器获取出来
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            // 调用各种通知对应的拦截器的invoke()方法
            return dm.interceptor.invoke(this);
        }
        else {
            return proceed();
        }
    }
    else {
        // 拦截器是ExposeInvocationInterceptor时会调用到这里
        // ExposeInvocationInterceptor的invoke()方法会先为当前线程保存方法调用器ReflectiveMethodInvocation
        // 然后再递归调用ReflectiveMethodInvocation的proceed()方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

ReflectiveMethodInvocationproceed() 方法中,会依次先调用所有拦截器的invoke() 方法,最后才调用到目标方法,但是实际情况下,有一些通知是需要在目标方法执行后或者目标方法抛出异常时才执行,所以可以推测,调用了拦截器的invoke() 方法不代表拦截器对应的通知逻辑会被执行,以及在拦截器的invoke() 方法中会在某个时间点递归的调用回ReflectiveMethodInvocationproceed() 方法。

为了方便理解,下面分别的以本示例中使用到的ExposeInvocationInterceptorMethodBeforeAdviceInterceptorAspectJAfterAdvice这三种拦截器进行说明。

  • ExposeInvocationInterceptorinvoke() 方法如下所示。
public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation oldInvocation = invocation.get();
    // 为当前线程保存方法调用器,即保存ReflectiveMethodInvocation
    invocation.set(mi);
    try {
        // 递归调用ReflectiveMethodInvocation的proceed()方法来执行其它拦截器或者目标方法
        return mi.proceed();
    }
    finally {
        invocation.set(oldInvocation);
    }
}

ExposeInvocationInterceptor只是为当前线程保存了方法调用器的引用。

  • MethodBeforeAdviceInterceptorinvoke() 方法如下所示。
public Object invoke(MethodInvocation mi) throws Throwable {
    // 前置通知的逻辑要先于目标方法执行,所以这里先执行前置通知的逻辑
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    // 递归调用ReflectiveMethodInvocation的proceed()方法来执行其它拦截器或者目标方法
    return mi.proceed();
}

MethodBeforeAdviceInterceptorinvoke() 方法只要调用到了,那么对应的前置通知的逻辑就会被执行,这一点符合前置通知在目标方法执行前执行,前置通知逻辑执行完毕后,会再调用回ReflectiveMethodInvocationproceed() 方法,以便调用其它拦截器和目标方法。

  • AspectJAfterAdviceinvoke() 方法如下所示。
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 递归调用ReflectiveMethodInvocation的proceed()方法来先执行其它拦截器或者目标方法
        return mi.proceed();
    }
    finally {
        // 后置通知的逻辑要在目标方法执行后再执行,所以这里将后置通知的执行放在了finally中
        // 也表明就算目标方法或者其它拦截器执行时抛出异常,后置通知的逻辑也是会执行到的
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

AspectJAfterAdviceinvoke() 方法中,将后置通知逻辑的调用放在了finally中,所以后置通知的逻辑一定会等到其它通知和目标方法执行后再执行。

那么到这里,方法调用器ReflectiveMethodInvocation调用拦截器和目标方法的流程已经形成了一个闭环,借助递归调用的特性,目标方法和拦截器都会被调用到,虽然目标方法的调用会在所有拦截器调用之后,但是目标方法的执行是会先于某些通知的执行的(比如后置通知)。

最后还需要说明,在ReflectiveMethodInvocationproceed() 方法中,使用了currentInterceptorIndex字段来标识当前调用到了第几个拦截器,初始值为-1,每次使用前先加1(即++currentInterceptorIndex),那么拦截器在集合中的位置实际是会影响拦截器的invoke() 方法的调用顺序,那么通过上面的源码分析,这个调用顺序的影响可以归纳如下。

  • 不同通知对应的拦截器在集合中的位置不会影响不同通知的调用顺序,比如前置通知逻辑的执行肯定会先于后置通知逻辑的执行;
  • 相同通知对应的拦截器在集合中的位置会影响相同通知的调用顺序,比如前置通知1在集合中的索引比前置通知2在集合中的索引小,那么前置通知1的逻辑的执行会先于前置通知2。

三. 时序图

AOP动态代理对象执行方法时,调用时序图如下所示。

详细分析Spring的AOP源码下篇

总结

SpringAOP的动态代理对象持有通知链和目标对象,那么在调用动态代理对象方法时,会先从通知链中找出能够作用于目标方法的Advisor,然后将每个符合条件的Advisor封装成MethodInvocation并加入集合,称MethodInvocation的集合为拦截器链,得到拦截器链后,会基于拦截器链创建方法调用器MethodInvocation,然后通过MethodInvocationproceed() 方法调用拦截器和目标方法的逻辑。