美文网首页深度解析Spring5源码
34--SpringAop获取增强(二)

34--SpringAop获取增强(二)

作者: 闲来也无事 | 来源:发表于2018-12-06 21:43 被阅读64次

    在上一篇结尾,我们得到了增强的提取工作交给了List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);。接着分析。

    1. getAdvisors获取增强简析
    @Override
    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        // 1、预处理工作,包括获取切面类,名称,验证等
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        validate(aspectClass);
    
        // 我们需要用一个装饰器包装MetadataAwareAspectInstanceFactory,这样它只会实例化一次。
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    
        // 2、提取增强
        // 2.1、获取切面类的所有方法,循环判断提取合适的切入点,并创建增强
        List<Advisor> advisors = new ArrayList<>();
        for (Method method : getAdvisorMethods(aspectClass)) {
            // 获取增强
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
    
        // If it's a per target aspect, emit the dummy instantiating aspect.
        // 2.2、处理perthis和pertarget切面类
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            // 创建SyntheticInstantiationAdvisor实例
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            // 将SyntheticInstantiationAdvisor实例加入到advisors集合首位,注意:不是替换
            advisors.add(0, instantiationAdvisor);
        }
    
        // Find introduction fields.
        // 2.3、处理引入,获取所有的引入并循环创建DeclareParentsAdvisor
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
    
        return advisors;
    }
    

    在该方法中提取工作一共分为了三步:提取普通增强、处理处理perthis和pertarget、引介增强。篇幅有限,只分析普通增强的处理过程,该过程也是大家最关心的过程。来看代码:

    @Override
    @Nullable
    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrderInAspect, String aspectName) {
    
        // 1、验证
        validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    
        // 2、提取切入点
        AspectJExpressionPointcut expressionPointcut = getPointcut(
                candidateAdviceMethod,
                aspectInstanceFactory.getAspectMetadata().getAspectClass());
    
        if (expressionPointcut == null) {
            return null;
        }
    
        // 3、创建增强
        return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }
    

    在该方法中,终于看到了核心的切点提取和创建增强。下面分别来看这两步是如何实现的。

    2.提取切入点
    @Nullable
    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        // 1、从候选切入点上找出增强表达式
        AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
    
        // 2、创建AspectJExpressionPointcut对象
        AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
        ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
        if (this.beanFactory != null) {
            ajexp.setBeanFactory(this.beanFactory);
        }
        return ajexp;
    }
    
    2.1 从候选切入点上找出增强表达式
    /**
     * Find and return the first AspectJ annotation on the given method
     * (there <i>should</i> only be one anyway...).
     */
    @SuppressWarnings("unchecked")
    @Nullable
    protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
        Class<?>[] classesToLookFor = new Class<?>[] {
                Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
        for (Class<?> c : classesToLookFor) {
            AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
            if (foundAnnotation != null) {
                return foundAnnotation;
            }
        }
        return null;
    }
    

    该过程主要通过findAnnotation方法完成,但是该方法调用较深,也不属于我们分析的范畴,感兴趣的同学可以自己跟踪调试。

    2.2 创建AspectJExpressionPointcut对象
    /**
     * Create a new AspectJExpressionPointcut with the given settings.
     * @param declarationScope the declaration scope for the pointcut
     * @param paramNames the parameter names for the pointcut
     * @param paramTypes the parameter types for the pointcut
     */
    public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
        this.pointcutDeclarationScope = declarationScope;
        if (paramNames.length != paramTypes.length) {
            throw new IllegalStateException(
                    "Number of pointcut parameter names must match number of pointcut parameter types");
        }
        this.pointcutParameterNames = paramNames;
        this.pointcutParameterTypes = paramTypes;
    }
    

    该创建过程比较简单,将提取的切点表达式的信息实例化为AspectJExpressionPointcut对象即可。

    3.创建增强
    public InstantiationModelAwarePointcutAdvisorImpl(
                AspectJExpressionPointcut declaredPointcut,
                Method aspectJAdviceMethod,
                AspectJAdvisorFactory aspectJAdvisorFactory,
                MetadataAwareAspectInstanceFactory aspectInstanceFactory,
                int declarationOrder,
                String aspectName) {
    
        this.declaredPointcut = declaredPointcut;
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
        this.methodName = aspectJAdviceMethod.getName();
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
        this.aspectJAdviceMethod = aspectJAdviceMethod;
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
        this.aspectInstanceFactory = aspectInstanceFactory;
        this.declarationOrder = declarationOrder;
        this.aspectName = aspectName;
    
        // 1、延迟初始化
        // 在Spring AOP中,切面类的实例只有一个,比如前面我们一直使用的MyAspect类,
        // 假设我们使用的切面类需要具有某种状态,以适用某些特殊情况的使用,比如多线程环境,此时单例的切面类就不符合我们的要求了。
        // 在Spring AOP中,切面类默认都是单例的,但其还支持另外两种多例的切面实例的切面,即perthis和pertarget,
        // 需要注意的是perthis和pertarget都是使用在切面类的@Aspect注解中的。
        // 这里perthis和pertarget表达式中都是指定一个切面表达式,其语义与前面讲解的this和target非常的相似,
        // perthis表示如果某个类的代理类符合其指定的切面表达式,那么就会为每个符合条件的目标类都声明一个切面实例;
        // pertarget表示如果某个目标类符合其指定的切面表达式,那么就会为每个符合条件的类声明一个切面实例。
        // 从上面的语义可以看出,perthis和pertarget的含义是非常相似的。如下是perthis和pertarget的使用语法:
        // perthis(pointcut-expression)
        // pertarget(pointcut-expression)
        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            // Static part of the pointcut is a lazy type.
            Pointcut preInstantiationPointcut = Pointcuts.union(
                    aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(),
                    this.declaredPointcut);
    
            // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
            // If it's not a dynamic pointcut, it may be optimized out
            // by the Spring AOP infrastructure after the first evaluation.
            this.pointcut = new PerTargetInstantiationModelPointcut(
                    this.declaredPointcut,
                    preInstantiationPointcut,
                    aspectInstanceFactory);
            this.lazy = true;
        }
        // 2、立刻初始化
        else {
            // A singleton aspect.
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            // 初始化增强
            this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
        }
    }
    

    创建过程中又涉及到perthis和pertarget,无需理会,还是先看单例模式下的创建过程:

    /**
     * 根据pointcut初始化增强
     * @param pointcut
     * @return
     */
    private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
        Advice advice = this.aspectJAdvisorFactory.getAdvice(
                this.aspectJAdviceMethod, pointcut,
                this.aspectInstanceFactory,
                this.declarationOrder,
                this.aspectName);
        return (advice != null ? advice : EMPTY_ADVICE);
    }
    
    @Override
    @Nullable
    public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
            MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    
        // 1、获取增强之前的处理
        Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        validate(candidateAspectClass);
    
        AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
    
        // If we get here, we know we have an AspectJ method.
        // Check that it's an AspectJ-annotated class
        if (!isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: " +
                    "Offending method '" + candidateAdviceMethod + "' in class [" +
                    candidateAspectClass.getName() + "]");
        }
    
    
        // 2、针对各种不同的增强,做不同的处理
        AbstractAspectJAdvice springAdvice;
        switch (aspectJAnnotation.getAnnotationType()) {
                // 1、前置增强
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
                // 2、后置增强
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
                // 3、后置返回增强
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
                // 4、后置异常增强
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
                // 5、环绕增强
            case AtAround:
                springAdvice = new AspectJAroundAdvice(
                        candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
                break;
                // 6、如果是Pointcut,则不做处理
            case AtPointcut:
                return null;
                // 7、未能满足case条件,抛出异常
            default:
                throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
        }
    
        // 3、获取增强方法之后,对增强方法进行配置
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrder);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        springAdvice.calculateArgumentBindings();
    
        return springAdvice;
    }
    

    看到这里,是不是有种豁然开朗的感觉,Spring针对各种不同的增强创建过程,做了不同的处理。到这里Spring创建增强的过程就完成了,对于不同的增强类型创建,大家可以自己debug跟踪,这里不一一赘述了。

    4.关于Advisor的疑问

    这两篇在介绍获取增强,但是最受获取到的并不是Advice而是Advisor,可能大家会有所疑问。举例说明一下:
    如果我们定义了一个DogAspect类,并用@AspectJ对其进行注解,那么该类仅仅代表一个切面类,会被Spring扫描并解析,仅此而已,该类不代表SpringAop概念中的切面。那么Spring如果通过解析该类得到具体的切面呢?

    首先,关于SpringAop中的切面概念,可以理解为 切面=连接点+增强

    其次,而标记了@AspectJ注解的类在被Spring解析的时候,

    1. 提取该类的方法上的切点表达式注解:例如-->@Pointcut("execution(* com.lyc.cn.v2.day07..(..))"),解析之后,就可以的到具体的切点.
    2. 提取该类的方法上的增强注解:例如:@Before("test()")解析之后,就可以得到具体的增强代码

    最后,通过第一步和第二步的操作,就可以得到切点+增强,那么自然就构成了一个切面

    但是Advisor接口里只包含了一个Advice,并且Advisor一般不直接提供给用户使用,所以这里也可以理解为获取增强,当然如果理解为切面也是没有问题的。

    相关文章

      网友评论

        本文标题:34--SpringAop获取增强(二)

        本文链接:https://www.haomeiwen.com/subject/wxmacqtx.html