前言

本篇文章是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() 办法调用拦截器和方针办法的逻辑。