Spring源碼解析 -- AOP原理(2)

源碼分析基於spring 4.3.x
本文承接上一篇文章對Spring AOP的分析,繼續分析spring如何對AOP中多個通知進行鏈式調用的。
關於閱讀源碼的思路,可參考 -- 如何閱讀java源碼java

《Spring源碼解析 -- AOP原理(1)》 中說了,JdkDynamicAopProxy實現了InvocationHandler,正是這個類調用​AOP通知中定義的加強方法。
JdkDynamicAopProxy#invokespring

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

        TargetSource targetSource = this.advised.targetSource;
        Class<?> targetClass = null;
        Object target = null;

        try {
            ... // #1

            Object retVal;

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

            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }

            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);    // #2

            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);    // #3
            }
            else {
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);    // #4
                retVal = invocation.proceed();    // #5
            }

            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {    // #6
                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);
            }
        }
    }

#1 處理equals/hashCode等方法,直接調用目標方法
#2 getInterceptorsAndDynamicInterceptionAdvice -- 獲取方法攔截器
#3 沒有方法攔截器,直接調用目標方法
#4 構造調用鏈ReflectiveMethodInvocation
#5 調用調用鏈
#6 處理特殊場景 -- 方法返回值爲this微信

AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice -> DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice源碼分析

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

    // This is somewhat tricky... We have to process introductions first,
    // but we need to preserve order in the ultimate list.
    List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

    for (Advisor advisor : config.getAdvisors()) {
        if (advisor instanceof PointcutAdvisor) {    
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {    // #1
                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);    // #2    
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();    
                if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {    // #3
                    if (mm.isRuntime()) {    // #4
                        // Creating a new object instance in the getInterceptors() method
                        // isn't a problem as we normally cache created chains.
                        for (MethodInterceptor interceptor : interceptors) {
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));    // #5
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));    // #6
                    }
                }
            }
        }
        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;
}

#1 判斷class是否匹配pointcut
#2 getInterceptors -- 將advisor轉化爲MethodInterceptor方法攔截器,MethodInterceptor#invoke負責調用被攔截的方法
#3 判斷method是否匹配pointcut
#4 是否運行時判斷
#5 添加InterceptorAndDynamicMethodMatcher到結果
#6 添加MethodInterceptor到結果ui

DefaultAdvisorAdapterRegistry#getInterceptors:this

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            if (adapter.supportsAdvice(advice)) {
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    }

有三個Advice沒有實現MethodInterceptor,經過DefaultAdvisorAdapterRegistry#adapters適配lua

public DefaultAdvisorAdapterRegistry() {
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }

回到JdkDynamicAopProxy#invoke方法#5步驟,調用調用鏈
ReflectiveMethodInvocation#proceedspa

public Object proceed() throws Throwable {
    //    We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {    // #1
        return invokeJoinpoint();    // #2
    }

    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);    //#3
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {    //#4
        ...
    }
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);    //#5
    }
}

#1 判斷是否到攔截器鏈末尾,注意currentInterceptorIndex初始值是-1
#2 攔截器已經調用完,調用被攔截的方法
#3 獲取下一個攔截器
#4 是否爲須要運行時判斷
#5 直接調用攔截器code

調用鏈ReflectiveMethodInvocation中的節點是攔截器MethodInterceptor。
ReflectiveMethodInvocation#proceed方法會獲取下一個攔截器,並調用MethodInterceptor#invoke方法。
注意,調用攔截器MethodInterceptor#invoke方法時會將調用鏈ReflectiveMethodInvocation做爲參數,攔截器MethodInterceptor#invoke執行完邏輯後,又調用ReflectiveMethodInvocation#proceed,繼續調用下一個攔截器。orm

看看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;    // #1
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);    // #2
    JoinPointMatch jpm = getJoinPointMatch(pmi);
    return invokeAdviceMethod(pjp, jpm, null, null);    // #3
}

#1 獲取調用鏈
#2 使用調用鏈構造一個MethodInvocationProceedingJoinPoint
#3 調用加強方法

protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
        throws Throwable {
    return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));    // #1
}

#1
invokeAdviceMethodWithGivenArgs -- 經過反射調用加強方法,就是執行Around通知
argBinding -- 負責參數綁定

AbstractAspectJAdvice#argBinding

protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
    calculateArgumentBindings();    //#1

    // AMC start
    Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
    int numBound = 0;

    if (this.joinPointArgumentIndex != -1) {    //#2
        adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
        numBound++;
    }
    else if (this.joinPointStaticPartArgumentIndex != -1) {
        adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
        numBound++;
    }

    if (!CollectionUtils.isEmpty(this.argumentBindings)) {    //#3
        // binding from pointcut match
        if (jpMatch != null) {
            PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
            for (PointcutParameter parameter : parameterBindings) {
                String name = parameter.getName();
                Integer index = this.argumentBindings.get(name);
                adviceInvocationArgs[index] = parameter.getBinding();
                numBound++;
            }
        }
        // binding from returning clause
        if (this.returningName != null) {
            Integer index = this.argumentBindings.get(this.returningName);
            adviceInvocationArgs[index] = returnValue;
            numBound++;
        }
        // binding from thrown exception
        if (this.throwingName != null) {
            Integer index = this.argumentBindings.get(this.throwingName);
            adviceInvocationArgs[index] = ex;
            numBound++;
        }
    }

    if (numBound != this.parameterTypes.length) {
        throw new IllegalStateException("Required to bind " + this.parameterTypes.length +
                " arguments, but only bound " + numBound + " (JoinPointMatch " +
                (jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)");
    }

    return adviceInvocationArgs;
}

#1 經過反射獲取參數名,返回名等信息,設置joinPointArgumentIndex,argumentBindings等屬性
#2 添加JoinPointMatch到加強方法的參數中
#3 添加被攔截方法參數,返回值,異常等數據到加強方法的參數中

在環繞通知中,咱們須要編寫以下代碼

@Around("...")
    public Object methodAroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed(joinPoint.getArgs());
        return result;
    }

這裏joinPoint.proceed(joinPoint.getArgs());調用了下一個攔截器(MethodInvocationProceedingJoinPoint#proceed會調用ReflectiveMethodInvocation#proceed),這樣才能繼續調用攔截器調用鏈。

@Before通知會調用MethodBeforeAdviceInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );    // #1
    return mi.proceed();    // #2
}

#1 執行@Before加強
#2 繼續攔截器調用鏈

@After通知會調用AspectJAfterThrowingAdvice

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

#1 先調用攔截器鏈
#2 執行@After加強

到這裏,spring aop的源碼解析就完成了。
這部分仍是挺複雜的,須要耐心點看源碼。

若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!

相關文章
相關標籤/搜索