前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring AOP(三) Advisor类架构

Spring AOP(三) Advisor类架构

原创
作者头像
程序员历小冰
修改2019-02-25 21:01:32
1.1K0
修改2019-02-25 21:01:32
举报
文章被收录于专栏:程序员历小冰

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框架中使用。

aopallinace类图
aopallinace类图

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

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

Advisor相关类图
Advisor相关类图

获取所有Advisor实例

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

代码语言:txt
复制
// 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实例。

代码语言:txt
复制
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实例。

代码语言:txt
复制
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函数,并且将这些方法作为参数。

代码语言:txt
复制
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通知。

代码语言:txt
复制
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函数。

代码语言:txt
复制
// 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后续文章很快就会更新,请大家继续关注。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Advisor类架构
  • 获取所有Advisor实例
  • 生成InstantiationModelAwarePointcutAdvisorImpl实例
  • 后记
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档