乐趣区

关于spring:SpringSpringAOP源码学习下

前言

本篇文章是 SpringAOP 的源码学习分享,分为高低两篇,上篇已知 SpringAOP 的切面类织入业务 bean 后,会为业务 bean 生成动静代理对象,这个动静代理对象中持有须要失效的所有告诉,叫做告诉链。本篇将对调用 AOP 动静代理对象时的整个流程进行学习,以探索切面中的相似于前置告诉或者后置告诉这种告诉办法是如何对指标 bean 的指标办法进行加强的。

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

注释

一. AOP 动静代理对象构造剖析

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

能够看到获取进去的 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,它们的关系能够用上面的类图示意。

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

通过类图能够晓得,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动静代理对象执行办法时,调用时序图如下所示。

总结

SpringAOP的动静代理对象持有告诉链和指标对象,那么在调用动静代理对象办法时,会先从告诉链中找出可能作用于指标办法的 Advisor,而后将每个符合条件的Advisor 封装成 MethodInvocation 并退出汇合,称 MethodInvocation 的汇合为拦截器链,失去拦截器链后,会基于拦截器链创立办法调用器 MethodInvocation,而后通过MethodInvocationproceed()办法调用拦截器和指标办法的逻辑。

退出移动版