Spring AOP(三) Advisor类架构

栏目: Java · 发布时间: 6年前

内容简介:Spring AOP是Spring的两大基石之一,不了解其基础概念的同学可以查看这两篇文章如果从代码执行角度来看,Spring AOP的执行过程分为四大步骤:由于这四个步骤涉及的源码量较大,一篇文章无法直接完全讲解完,本篇文章只讲解第一步

Spring AOP是Spring的两大基石之一,不了解其基础概念的同学可以查看这两篇文章 AOP基本概念修饰者模式和JDK Proxy

如果从代码执行角度来看,Spring AOP的执行过程分为四大步骤:

  • 步骤一:Spring框架生成Advisor实例,可以是 @Aspect@Async 等注解生成的实例,也可以是 程序员 自定义的 AbstractAdvisor 子类的实例。
  • 步骤二:Spring框架在目标实例初始化完成后,也就是使用 BeanPostProcessorpostProcessAfterInitialization 方法,根据Advisor实例中切入点 Pointcut 的定义,选择出适合该目标对象的Advisor实例。
  • 步骤三:Spring框架根据Advisor实例生成代理对象。
  • 步骤四:调用方法执行过程时,Spring框架执行Advisor实例的通知 Advice 逻辑。

由于这四个步骤涉及的源码量较大,一篇文章无法直接完全讲解完,本篇文章只讲解第一步 Advisor 实例生成的源码分析。接下来的文章我们就依次讲解一下后续步骤中比较关键的逻辑。

Advisor类架构

Spring中有大量的机制都是通过AOP实现的,比如说 @Async 的异步调用和 @Transational 。此外,用户也可以使用 @Aspect 注解定义切面或者直接继承 AbstractPointcutAdvisor 来提供切面逻辑。上述这些情况下,AOP都会生成对应的Advisor实例。

我们先来看一下Advisor的相关类图。首先看一下 org.aopalliance 包下的类图。aopalliance是AOP组织下的公用包,用于AOP中方法增强和调用,相当于一个jsr标准,只有接口和异常,在AspectJ、Spring等AOP框架中使用。

Spring AOP(三) Advisor类架构

aopalliance定义了AOP的通知 Advice 和连接点 Joinpoint 接口,并且还有继承上述接口的 MethodInterceptorMethodInvocation 。这两个类相信大家都很熟悉。

然后我们来看一下Spring AOP中Advisor相关的类图。Advisor是Spring AOP独有的概念,比较重要的类有 AbstractPointcutAdvisorInstantiationModelAwarePointcutAdvisor 。相关的讲解都在图中表明了,如果这张图中的概念和类同学们都熟识,那么对AOP的了解就已经很深入了。

Spring AOP(三) Advisor类架构

获取所有 Advisor 实例

AOP生成Advisor实例的函数入口是 AbstractAdvisorAutoProxyCreatorfindCandidateAdvisors 函数。

// AbstractAdvisorAutoProxyCreator.java 找出当前所有的Advisor
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

// AnnotationAwareAspectJAutoProxyCreator,是AbstractAdvisorAutoProxyCreator的子类
@Override
protected List<Advisor> findCandidateAdvisors() {
    // 调用父类的findCandidateAdvisor函数,一般找出普通的直接
    // 继承Advisor接口的实例,比如说`@Async`所需的`AsyncAnnotationAdvisor`
    List<Advisor> advisors = super.findCandidateAdvisors();
    // 为AspectJ的切面构造Advisor,也就是说处理@Aspect修饰的类,生成上文中说的`InstantiationModelAwarePointcutAdvisor`实例
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

相关的ProxyCreator也有一个类体系,不过太过繁杂,而且重要性不大,我们就先略过,直接将具体的类。由上边代码可知 AbstractAdvisorAutoProxyCreatorfindCandidateAdvisors 函数是直接获取Spring容器中的 Advisor 实例,比如说 AsyncAnnotationAdvisor 实例,或者说我们自定义的 AbstractPointcutAdvisor 的子类实例。 AdvisorRetrievalHelperfindAdvisorBeans 函数通过 BeanFactorygetBean 获取了所有类型为 Advisor 的实例。

AnnotationAwareAspectJAutoProxyCreator 看其类名就可知,是与AspectJ相关的创建器,用来获取 @Aspect 定义的Advisor实例,也就是 InstantiationModelAwarePointcutAdvisor 实例。

接下去我们看一下 BeanFactoryAspectJAdvisorsBuilderbuildAspectJAdvisors 函数,它根据 @Aspect 修饰的切面实例生成对应的 Advisor 实例。

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;
    // 第一次初始化,synchronized加双次判断,和经典单例模式的写法一样。
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                // Spring源码并没有buildAspectJAdvisorsFirstly函数,为了方便理解添加。
                // 获取aspectNames,创建Advisor实例,并且存入aspectFactoryCache缓存
                return buildAspectJAdvisorsFirstly();
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    // 遍历aspectNames,依次获取对应的Advisor实例,或者是MetadataAwareAspectInstanceFactory生成的Advisor实例
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        // cache可以取到实例,该Advisor是单例的
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            // 取得Advisor对应的工厂类实例,再次生成Advisor实例,该Advisor是多实例的。
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

buildAspectJAdvisors 函数执行时分为两种情况,第一个未初始化时,也就是 aspectNames 为null时,执行 buildAspectJAdvisorsFirstly 进行第一次初始化,在这一过程中生成切面名称列表 aspectBeanNames 和要返回的 Advisor 列表,并且将生成的 Advisor 实例放置到 advisorsCache 中。

第二种情况则是已经初始化后再次调用,遍历 aspectNames ,从 advisorsCache 取出对应的 Advisor 实例,或者从 advisorsCache 取出Advisor对应的工厂类对象,再次生成 Advisor 实例。

public List<Advisor> buildAspectJAdvisorsFirstly() {
    List<Advisor> advisors = new ArrayList<>();
    List<String> aspectNames = new ArrayList<>();
    // 调用BeanFactoryUtils获取所有bean的名称
    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Object.class, true, false);
    for (String beanName : beanNames) {
        if (!isEligibleBean(beanName)) {
            continue;
        }
        // 获取对应名称的bean实例
        Class<?> beanType = this.beanFactory.getType(beanName);
        if (beanType == null) {
            continue;
        }
        /**
         * AbstractAspectJAdvisorFactory类的isAspect函数来判断是否为切面实例
         * 判断条件为是否被@Aspect修饰或者是由AspectJ编程而来。
         */
        if (this.advisorFactory.isAspect(beanType)) {
            aspectNames.add(beanName);
            AspectMetadata amd = new AspectMetadata(beanType, beanName);
            // 切面的属性为单例模式
            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                MetadataAwareAspectInstanceFactory factory =
                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                // 获取一个切面中所有定义的Advisor实例。一个切面可以定义多个Advisor。
                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                // 单例模式,只需要将生成的Advisor添加到缓存
                if (this.beanFactory.isSingleton(beanName)) {
                    this.advisorsCache.put(beanName, classAdvisors);
                }
                // 多实例模式,需要保存工厂类,便于下一次再次生成Advisor实例。
                else {
                    this.aspectFactoryCache.put(beanName, factory);
                }
                advisors.addAll(classAdvisors);
            }
            else {
                MetadataAwareAspectInstanceFactory factory =
                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                this.aspectFactoryCache.put(beanName, factory);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
        }
    }
    this.aspectBeanNames = aspectNames;
    return advisors;
}

buildAspectJAdvisorsFirstly 函数的逻辑如下:

  • 首先使用 BeanFactoryUtils 获取了BeanFactory中所有的BeanName,然后进而使用 BeanFactory 获取所有的Bean实例。
  • 遍历Bean实例,通过 ReflectiveAspectJAdvisorFactoryisAspect 函数判断该实例是否为切面实例,也就是被 @Aspect 注解修饰的实例。
  • 如果是,则使用 ReflectiveAspectJAdvisorFactory ,根据切面实例的定义来生成对应的多个 Advisor 实例,并且将其加入到 advisorsCache 中。

生成InstantiationModelAwarePointcutAdvisorImpl实例

ReflectiveAspectJAdvisorFactorygetAdvisors 函数会获取 @Aspect 修饰的实例中所有 没有@Pointcut 修饰的方法,然后调用 getAdvisor 函数,并且将这些方法作为参数。

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {

    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    // 获得该方法上的切入点条件表达式
    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }
    // 生成Advisor实例
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 获得该函数上@Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing注解的信息
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    // 没有上述注解,则直接返回
    if (aspectJAnnotation == null) {
        return null;
    }

    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;
}

getAdvisor 函数就是根据作为参数传入的切面实例的方法上的注解来生成Advisor实例,也就是 InstantiationModelAwarePointcutAdvisorImpl 对象。依据方法上的切入点表达式生成 AspectJExpressionPointcut

我们都知道 PointcutAdvisor 实例中必然有一个 PointcutAdvice 实例。修饰在方法上的注解包括: @Pointcut , @Around , @Before , @After , @AfterReturning@AfterThrowing ,所以 InstantiationModelAwarePointcutAdvisorImpl 会依据不同的不同的注解生成不同的 Advice 通知。

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
        Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    // .... 省略成员变量的直接赋值

    // 单例模式时
    this.pointcut = this.declaredPointcut;
    this.lazy = false;
    // 按照注解解析 Advice
    this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}

InstantiationModelAwarePointcutAdvisorImpl 的构造函数中会生成对应的 PointcutAdviceinstantiateAdvice 函数调用了 ReflectiveAspectJAdvisorFactorygetAdvice 函数。

// ReflectiveAspectJAdvisorFactory
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    validate(candidateAspectClass);
    // 获取 Advice 注解
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // 检查是否为AspectJ注解
    if (!isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" +
                candidateAspectClass.getName() + "]");
    }
    
    AbstractAspectJAdvice springAdvice;
    // 按照注解类型生成相应的 Advice 实现类
    switch (aspectJAnnotation.getAnnotationType()) {
        case AtPointcut:
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        case AtAround: // @Before 生成 AspectJMethodBeforeAdvice
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtBefore: // @After 生成 AspectJAfterAdvice
            springAdvice = new AspectJMethodBeforeAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfter: // @AfterReturning 生成 AspectJAfterAdvice
            springAdvice = new AspectJAfterAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfterReturning: // @AfterThrowing 生成 AspectJAfterThrowingAdvice
            springAdvice = new AspectJAfterReturningAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;
        case AtAfterThrowing: // @Around 生成 AspectJAroundAdvice
            springAdvice = new AspectJAfterThrowingAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }
 // 配置Advice
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    // 获取方法的参数列表方法
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 设置参数名称
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();

    return springAdvice;
}

至此,Spring AOP就获取了容器中所有的 Advisor 实例,下一步在每个实例初始化完成后,根据这些 AdvisorPointcut 切入点进行筛选,获取合适的 Advisor 实例,并生成代理实例。

后记

Spring AOP后续文章很快就会更新,请大家继续关注。

Spring AOP(三) Advisor类架构


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java Message Service API Tutorial and Reference

Java Message Service API Tutorial and Reference

Hapner, Mark; Burridge, Rich; Sharma, Rahul / 2002-2 / $ 56.49

Java Message Service (JMS) represents a powerful solution for communicating between Java enterprise applications, software components, and legacy systems. In this authoritative tutorial and comprehens......一起来看看 《Java Message Service API Tutorial and Reference》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具