Spring系列之AOP分析之获取Advice的过程(四)

我们在前面的文章中分析了从切面类中获取Advisor的过程,我们最后创建的Advisor实例为:InstantiationModelAwarePointcutAdvisorImpl,它是一个Advisor和PointcutAdvisor的实现类,所以我们可以从这个类中获取Advice和Pointcut。从之前的分析中我们也看到了Pointcut的赋值,在这一篇文章中我们将会具体分析Advice的创建过程。
我们在上一篇文章的末尾说到了这一段代码可以实例化Advice。我们来看看这个方法的代码:

this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    //入参为切点表达式类
    //这里是通过调用aspectJAdvisorFactory来获取Advice
    //aspectJAdvisorFactory的实例是ReflectiveAspectJAdvisorFactory所以最终我们还是要到
    //ReflectiveAspectJAdvisorFactory中去分析Advice的获取过程
    //ReflectiveAspectJAdvisorFactory真是一个重要的类啊Advisor和Advice的获取都是在这个类中完成的
    //入参为:通知方法、切点表达式类、切面实例、切面的一个顺序、切面类名
    //倒腾了倒腾去基本上还是这几个参数
    return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    }

ReflectiveAspectJAdvisorFactory中getAdvice方法的代码如下

    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
        //切面类 带有@Aspect注解的类
        Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        //验证切面类 不再分析了
        validate(candidateAspectClass);
        //又来一遍 根据通知方法 获取通知注解相关信息
        //在获取Advisor的方法 我们已经见过这个调用。这个在Spring的AnnotationUtils中会缓存方法的注解信息
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
        //再去校验一遍 如果不是切面类 则抛出异常
        if (!isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: " +
                    "Offending method '" + candidateAdviceMethod + "' in class [" +
                    candidateAspectClass.getName() + "]");
        }
        AbstractAspectJAdvice springAdvice;
        //在上一篇文章的分析中 我们说过在AspectJAnnotation中会存放通知类型
        switch (aspectJAnnotation.getAnnotationType()) {
            //如果是前置通知,则直接创建AspectJMethodBeforeAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //如果是后置通知,则直接创建AspectJAfterAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //如果是后置返回通知,则直接创建AspectJAfterReturningAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                //设置后置返回值的参数name
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
            //如果是后置异常通知,则直接创建AspectJAfterThrowingAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                //设置后置异常通知 异常类型参数name
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            //如果是后置异常通知,则直接创建AspectJAfterThrowingAdvice实例
            //入参为:通知方法、切点表达式、切面实例
            case AtAround:
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
            //如果是切点方法,则什么也不做
            case AtPointcut:
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            //上面的那几种情况都不是的话,则抛出异常
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method: " + candidateAdviceMethod);
        }

        // Now to configure the advice...
        //切面的名字
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrder);
        //通知注解中的参数名
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        //参数绑定
        springAdvice.calculateArgumentBindings();
        return springAdvice;
    }

上面即是获取Advice的过程。我们简单的看一下calculateArgumentBindings这个方法做了什么事:
calculateArgumentBindings

    public synchronized final void calculateArgumentBindings() {
        //如果已经进行过参数绑定了  或者通知方法中没有参数
        if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
            return;
        }
        
        int numUnboundArgs = this.parameterTypes.length;
        //通知方法参数类型
        Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
        //如果第一个参数是JoinPoint或者ProceedingJoinPoint
        if (maybeBindJoinPoint(parameterTypes[0]) || 
        //这个方法中还有一个校验 即只有在环绕通知中第一个参数类型才能是ProceedingJoinPoint
        maybeBindProceedingJoinPoint(parameterTypes[0])) {
            numUnboundArgs--;
        }
        //如果第一个参数是JoinPoint.StaticPart
        else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
            numUnboundArgs--;
        }

        if (numUnboundArgs > 0) {
            //进行参数绑定 绑定过程略复杂
            //常见的场景是我们使用 后置返回通知和后置异常通知的时候 需要指定 returning和throwing的值 
            bindArgumentsByName(numUnboundArgs);
        }
        this.argumentsIntrospected = true;
    }

通过前面的分析我们可以了解到一个切面中的通知方法会生成一个Advisor实例(如InstantiationModelAwarePointcutAdvisorImpl,其实这个也是我们在SpringAOP中最常用的一个Advisor实现类),在生成这个Advisor实例的过程中会创建一个相应的Advice实例! 一个通知方法---->一个Advisor(包含Pointcut)------>一个Advice!
PS:这里只是一个生成Advice的地方,在其他的地方也会生成Advice,我们在以后再分析。

上一篇:[FAQ] uni-app 不支持 v-cloak 情况下如何处理 v-if 页面闪烁问题


下一篇:cordova混合App开发:Cordova+Vue实现Android (环境搭建)