Spring系列之AOP分析之對通知方法的執行過程(九)

轉載請註明出處:https://blog.csdn.net/zknxx/article/details/80261327
我們在上一篇文章中說到了前置通知的方法調用AspectJMethodBeforeAdvice#before,在這個before方法中又調用了invokeAdviceMethod這個方法,invokeAdviceMethod這個方法在AspectJMethodBeforeAdvice的父類AbstractAspectJAdvice中。AbstractAspectJAdvice這個是Aspect的所有通知類型的共同父類。關於AbstractAspectJAdvice中的invokeAdviceMethod方法,有兩個重載的方法。前置通知、後置通知、異常通知、後置返回通知都是用的AbstractAspectJAdvice#invokeAdviceMethod(org.aspectj.weaver.tools.JoinPointMatch, java.lang.Object, java.lang.Throwable)這個方法,環繞通知用的是:AbstractAspectJAdvice#invokeAdviceMethod(org.aspectj.lang.JoinPoint, org.aspectj.weaver.tools.JoinPointMatch, java.lang.Object, java.lang.Throwable)這個方法。這兩個重載方法的區別是:後置通知調用的方法多了一個JoinPoint的參數。
invokeAdviceMethod方法的源碼如下:

    //這三個參數 JoinPointMatch 都是相同的
    //returnValue 當執行後置返回通知的時候 傳值 其他爲null
    //Throwable  當執行後置異常通知的時候 傳值,其他爲null
    protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
        return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
    }
    //重載的方法 這個 JoinPoint 是ProceedingJoinPoint
    protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
            throws Throwable {

        return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
    }

我們先看getJoinPoint這個方法,其源碼如下:

    protected JoinPoint getJoinPoint() {
        return currentJoinPoint();
    }
    public static JoinPoint currentJoinPoint() {
        //這裏就不用再多說了  獲取當前的MethodInvocation 即ReflectiveMethodInvocation的實例
        MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
        if (!(mi instanceof ProxyMethodInvocation)) {
            throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
        }
        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
        //JOIN_POINT_KEY 的值 爲 JoinPoint.class.getName()
        //從ReflectiveMethodInvocation中獲取JoinPoint 的值
        //這裏在第一次獲取的時候 獲取到的 JoinPoint是null 
        //然後把下面創建的MethodInvocationProceedingJoinPoint放入到ReflectiveMethodInvocation的userAttributes中
        //這樣在第二次獲取的是 就會獲取到這個 MethodInvocationProceedingJoinPoint
        JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
        if (jp == null) {
            jp = new MethodInvocationProceedingJoinPoint(pmi);
            pmi.setUserAttribute(JOIN_POINT_KEY, jp);
        }
        return jp;
    }

下面我們來看一下argBinding這個方法的作用和內容。從名字我們可以猜測這個方法的作用應該是進行參數綁定用的,我們來看一下:

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

        // AMC start
        Object[] adviceInvocationArgs = new Object[this.parameterTypes.length];
        int numBound = 0;
        //這個默認值是 -1 重新賦值是在calculateArgumentBindings中進行的
        if (this.joinPointArgumentIndex != -1) {
            adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
            numBound++;
        }
        else if (this.joinPointStaticPartArgumentIndex != -1) {
            adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
            numBound++;
        }
        //這裏主要是取通知方法中的參數類型 是除了 JoinPoint和ProceedingJoinPoint參數之外的參數
        //如異常通知參數 返回通知參數
        if (!CollectionUtils.isEmpty(this.argumentBindings)) {
            // 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;
    }

calculateArgumentBindings

    public synchronized final void calculateArgumentBindings() {
        // The simple case... nothing to bind.
        //通知方法沒有參數直接返回
        if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
            return;
        }

        int numUnboundArgs = this.parameterTypes.length;
        Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
        //從這裏可以看出來我們的JoinPoint和ProceedingJoinPoint要放在通知方法的第一個參數
        if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
            numUnboundArgs--;
        }
        else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
            numUnboundArgs--;
        }
        //這裏是對其他的參數的處理  處理過程還是比較複雜一點的 這裏不再多說了。
        if (numUnboundArgs > 0) {
            // need to bind arguments by name as returned from the pointcut match
            bindArgumentsByName(numUnboundArgs);
        }
        this.argumentsIntrospected = true;
    }

這裏還有再說一下AbstractAspectJAdvice這個類的構造函數,這個類只有這一個構造函數

    public AbstractAspectJAdvice(
            Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
        //通知方法不能爲空
        Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
        //切面類
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
        //通知方法的名字
        this.methodName = aspectJAdviceMethod.getName();
        //通知方法參數
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
        //通知方法
        this.aspectJAdviceMethod = aspectJAdviceMethod;
        //切點類
        this.pointcut = pointcut;
        //切面實例的工廠類
        this.aspectInstanceFactory = aspectInstanceFactory;
    }

在創建通知類實例的時候,進行了上面的賦值的動作,把和通知相關的方法都傳了進來。最後我們來看一下invokeAdviceMethodWithGivenArgs這個方法的內容:

    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
        Object[] actualArgs = args;
        //判斷通知方法是否有參數
        if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
            actualArgs = null;
        }
        try {
            ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
            // TODO AopUtils.invokeJoinpointUsingReflection
            //反射調用通知方法
            //this.aspectInstanceFactory.getAspectInstance()獲取的是切面的實例
            return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
        }
        catch (IllegalArgumentException ex) {
            throw new AopInvocationException("Mismatch on arguments to advice method [" +
                    this.aspectJAdviceMethod + "]; pointcut expression [" +
                    this.pointcut.getPointcutExpression() + "]", ex);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }

轉載請註明出處:https://blog.csdn.net/zknxx/article/details/80261327

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章