Spring AOP源码解析-核心组件解析及筛选合适的通知器

一.简介

首先,我们先看这篇文章:AOP详解,从这篇文章中回顾了关于AOP的一些专业术语。从这篇文章开始,我将会对Spring AOP部分的源码来进行解析,本文主要分析Spring AOP是如何为目标bean筛选合适的通知器。

二.核心组件解析

2.1 概述

Pointcut:用于定义拦截目标集合,祖先接口为org.springframework.aop.Pointcut

Advice:用于定义拦截行为。祖先接口为org.aopalliance.aop.Advice,该接口只是标识接口,应用中可直接实现BeforeAdvice ,ThrowsAdvice,MethodInterceptor ,AfterReturningAdvice ,IntroductionInterceptor 等子接口

Advisor:充当Advice和Pointcut的适配器,类似使用Aspect的@Aspect注解的类(前一章节所述)。一般有advice和pointcut属性。祖先接口为org.springframework.aop.Advisor,应用中可直接使用org.springframework.aop.support.DefaultPointcutAdvisor

2.2 Pointcut

Pointcut决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut来定义需要增强的方法的集合,这些集合可以按照一定的规则来完成。在这种情况下,Pointcut通常意味着标识方法,例如,这些需要增强的地方可以由某个正则表达式进行表示,或根据某个方法名进行匹配。

源码如下:

public interface Pointcut {

    /** 返回一个类型过滤器 */
    ClassFilter getClassFilter();

    /** 返回一个方法匹配器 */
    MethodMatcher getMethodMatcher();

    Pointcut TRUE = TruePointcut.INSTANCE;
}

Pointcut 接口中定义了两个接口,分别用于返回类型过滤器和方法匹配器。下面我们再来看一下类型过滤器和方法匹配器接口的定义:

public interface ClassFilter {

    /**
     * 判断切入点于给定的接口或目标类是否类型匹配
     */
    boolean matches(Class<?> clazz);

    ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

public interface MethodMatcher {

    /**
     * 检查是否有此方法的静态匹配
     */
    boolean matches(Method method, Class<?> targetClass);

    /**
     * 是否运行时
     */
    boolean isRuntime();

    /**
     * 检查是否有此方法的运行时(动态)匹配
     */
    boolean matches(Method method, Class<?> targetClass, Object... args);

    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

Pointcut接口作为SpringAop中对AOP的最顶层抽象,主要负责对系统的相应的Joinpoint进行捕捉,并且提供了一个TruePointcut实例,当Pointcut为TruePointcut类型时,则会忽略所有的匹配条件,对系统中所有的对象进行Joinpoint所定义的规则进行匹配;

ClassFilter与MethodMatcher分别用于在不同的级别上限定Joinpoint的匹配范围,满足不同粒度的匹配,ClassFilter限定在类级别上,MethodMatcher限定在方法级别上;但是SpringAop主要支持在方法级别上的匹配,所以对类级别的匹配支持相对简单一些;
当传入的clazz与Pointcut规定的类型一致时,则返回true,否则返回false,返回为true时,则表示对这个类进行植入操作,当类型对Joinpoint的匹配不产生影响的时候,可以让Pointcut接口中的ClassFilter getClassFilter()方法直接返回TrueClassFilter.INSTANCE,则表示对系统中的所有对象进行Joinpoint匹配;

MethodMatcher接口通过重载定义了两个matches()方法,两个参数的matches()被称为静态匹配,在匹配条件不是太严格时使用,可以满足大部分场景的使用,称之为静态的主要是区分为三个参数的matches()方法需要在运行时动态的对参数的类型进行匹配;两个方法的分界线就是boolean isRuntime()方法,进行匹配时先用两个参数的matches()方法进行匹配,若匹配成功,则检查boolean isRuntime()的返回值,若为true,则调用三个参数的matches()方法进行匹配(若两个参数的都匹配不中,三个参数的必定匹配不中),比如需要统计用户登录次数时,那么登录传入的参数就是可以忽略的,则调用两个参数的matches()方法就已经足够了,但是若要在登陆时对用户账号执行特殊的操作(如赋予特殊的操作权限),就需要对参数进行一个类似于检验的操作,就需要调用三个参数的matches()进行匹配。(上面三段内容来自:SpringAop中的Pointcut、ClassFilter、MethodMatcher接口的定义

接下来简单介绍一下Spring提供的切点类型:

  • 静态方法切点:org.springframework.aop.support.StaticMethodMatcherPointcut,静态方法切点的抽象基类,默认情况下匹配所有的类。最常用的两个子类NameMatchMethodPointcut和 AbstractRegexpMethodPointcut , 前者提供简单字符串匹配方法签名,后者使用正则表达式匹配方法签名。
  • 动态方法切点:org.springframework.aop.support.DynamicMethodMatcherPointcut,动态方法切点的抽象基类,默认情况下匹配所有的类
  • 注解切点:org.springframework.aop.support.annotation.AnnotationMatchingPointcut
  • 表达式切点:org.springframework.aop.support.ExpressionPointcut,提供了对AspectJ切点表达式语法的支持
  • 流程切点:org.springframework.aop.support.ControlFlowPointcut,该切点是一个比较特殊的节点,它根据程序执行的堆栈信息查看目标方法是否由某一个方法直接或间接发起调用,一次来判断是否为匹配的链接点
  • 复合切点:org.springframework.aop.support.ComposablePointcut,该类是为实现创建多个切点而提供的操作类

2.3 Advisor

完成对目标方法的切面通知设计(Advice)和关注点设计(Pointcut)以后,需要一个对象将它们结合起来,完成这个的作用就是Advisor(通知器)。通过Advisor,可以定义应该使用哪个通知并在哪个关注点上使用它,也就是通过Advisor,把Advice和Pointcut结合起来,这个结合为使用IOC容器配置AOP应用,或者说即开即用地使用AOP基础设施,提供了便利。接口定义如下:

public interface Advisor {

    Advice getAdvice();
    boolean isPerInstance();
}

public interface PointcutAdvisor extends Advisor {

    Pointcut getPointcut();
}

Advisor 中有一个 getAdvice 方法,用于返回通知。PointcutAdvisor 在 Advisor 基础上,新增了 getPointcut 方法,用于返回切点对象。因此 PointcutAdvisor 的实现类即可以返回切点,也可以返回通知,所以说 PointcutAdvisor 和切面的功能相似。

切面可以分为3类:一般切面、切点切面、引介切面

  • 一般切面Advisor
    org.springframework.aop.Advisor代表一般切面,仅包含一个Advice ,因为Advice包含了横切代码和连接点信息,所以Advice本身一个简单的切面,只不过它代表的横切的连接点是所有目标类的所有方法,因为这个横切面太宽泛,所以一般不会直接使用。

  • 切点切面PointcutAdvisor
    org.springframework.aop.PointcutAdvisor ,代表具有切点的切面,包括Advice和Pointcut两个类,这样就可以通过类、方法名以及方位等信息灵活的定义切面的连接点,提供更具实用性的切面。PointcutAdvisor主要有6个具体的实现类:

    1. DefaultPointcutAdvisor:最常用的切面类型,它可以通过任意Pointcut和Advice定义一个切面,唯一不支持的就是引介的切面类型,一般可以通过扩展该类实现自定义的切面
    2. NameMatchMethodPointcutAdvisor:通过该类可以定义按方法名定义切点的切面
    3. AspectJExpressionPointcutAdvisor:用于AspectJ切点表达式定义切点的切面
    4. StaticMethodMatcherPointcutAdvisor:静态方法匹配器切点定义的切面,默认情况下匹配所有的的目标类
    5. AspectJPointcutAdvisor:用于AspectJ语法定义切点的切面
  • 引介切面IntroductionAdvisor
    org.springframework.aop.IntroductionAdvisor代表引介切面, 引介切面是对应引介增强的特殊的切面,它应用于类层上面,所以引介切点使用ClassFilter进行定义。

三.源码分析

3.1 AOP入口分析

首先,我们要思考一个问题,Spring是如何将AOP和IOC模块整合到一起的?

在之前的文章:Bean的生命周期,我们了解到bean的整个生命周期过程,在这个过程中,Spring就将AOP和IOC模块整合到一起。即通过扩展BeanPostProcessor接口,Spring AOP抽象代理创建器实现了BeanPostProcessor接口,并在bean初始化后置过程中向bean织入通知,即Spring加载这个bean时会在实例化前调用其postProcessAfterInitialization方法,这个方法的入口可以查看这篇文章:余下的初始化工作的处理器应用。

Spring AOP 生成代理类的逻辑是在 AbstractAutoProxyCreator 相关子类中实现的,比如 DefaultAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator 等。上面说了 BeanPostProcessor 为拓展留下了可能,这里 AbstractAutoProxyCreator 就将可能变为了现实。AbstractAutoProxyCreator 实现了 BeanPostProcessor 接口,这样 AbstractAutoProxyCreator 可以在 bean 初始化时做一些事情。光继承这个接口还不够,继承这个接口只能获取 bean,要想让 AOP 生效,还需要拿到切面对象(包含 Pointcut 和 Advice)才行。所以 AbstractAutoProxyCreator 同时继承了 BeanFactoryAware 接口,通过实现该接口,AbstractAutoProxyCreator 子类就可拿到 BeanFactory,有了 BeanFactory,就可以获取 BeanFactory 中所有的切面对象了。有了目标对象 bean,所有的切面类,此时就可以为 bean 生成代理对象了。

下面,就来分析相关源码,如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    @Override
    /** bean 初始化后置处理方法 */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 如果需要,为 bean 生成代理对象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }

        /*
         * 如果是基础设施类(Pointcut、Advice、Advisor 等接口的实现类),或是应该跳过的类,
         * 则不应该生成代理,此时直接返回 bean
         */ 
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            // 将 <cacheKey, FALSE> 键值对放入缓存中,供上面的 if 分支使用
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 为目标 bean 查找合适的通知器
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        /*
         * 若 specificInterceptors != null,即 specificInterceptors != DO_NOT_PROXY,
         * 则为 bean 生成代理对象,否则直接返回 bean
         */ 
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 创建代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            /*
             * 返回代理对象,此时 IOC 容器输入 bean,得到 proxy。此时,
             * beanName 对应的 bean 是代理对象,而非原始的 bean
             */ 
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        // specificInterceptors = null,直接返回 bean
        return bean;
    }
}

总结一下这个入口方法的工作流程:

  1. 若bean为AOP基础设施类(Pointcut、Advice、Advisor 等接口的实现类),则直接返回
  2. 为bean查找合适的通知器
  3. 若通知数组不为空,则为bean生成代理对象,并返回该对对象
  4. 若数组为空,则返回原始bean

3.2 筛选合适的通知器

在向bean织入通知之前,我们首先要为bean筛选出合适的通知器(通知器持有通知)。如何筛选呢?方法有很多,比如我们可以通过正则表达式来匹配方法名,当然更多的是使用AspectJ表达式来进行匹配,下面来看一下使用AspectJ表达式筛选通知器的过程,如下:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
    // 查找合适的通知器
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 查找所有的通知器
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    /*
     * 筛选可应用在 beanClass 上的 Advisor,通过 ClassFilter 和 MethodMatcher
     * 对目标类和方法进行匹配
     */
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 拓展操作
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

对于指定bean的通知器的获取一定是包含两个步骤的,获取所有通知器以及寻找所有通知器中适用bean的通知器并应有,那么findCandidateAdvisors和findAdvisorsThatCanApply便是做了这两件事情。当然,如果无法找到对应的通知器,便返回DO_NOT_PROXY,其中DO_NOT_PROXY==null。

3.2.1 查找通知器

Spring提供了两种配置AOP的方式,一种是通过XML进行配置,另一种是基于注解的。详细可以查看:AOP实验

由于我们分析的是使用基于注解进行AOP,所以对于findCandidateAdvisors的实现其实是由AnnotationAwareAspectJAutoProxyCreator类完成的,现在,对源码进行解析:

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

    //...

    @Override
    protected List<Advisor> findCandidateAdvisors() {
        // 调用父类方法从容器中查找所有的通知器
        List<Advisor> advisors = super.findCandidateAdvisors();
        // 解析 @Aspect 注解,并构建通知器
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        return advisors;
    }

    //...
}

AnnotationAwareAspectJAutoProxyCreator覆写了父类的方法findCandidateAdvisors,并增加了一步操作,即解析@Aspect注解,并构建成通知器。下面来分析一下父类findCandidateAdvisors方法的逻辑,然后再来分析buildAspectJAdvisors方法的逻辑。

3.2.1.1 findCandidateAdvisors

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

    private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;
    
    //...

    protected List<Advisor> findCandidateAdvisors() {
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

    //...
}

从上面源码可以看出,AbstractAdvisorAutoProxyCreator 中的findCandidateAdvisors是个空壳方法,所有逻辑封装在BeanFactoryAdvisorRetrievalHelper 的findAdvisorBeans方法中。BeanFactoryAdvisorRetrievalHelper 可以理解为从bean容器中获取Advisor的帮助类,findAdvisorBeans则理解为查找Advisor类型的bean,这个方法的就是从bean容器中将Advisor类型的bean查找出来。下来分析一下这个方法的源码,如下:

public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = null;
    synchronized (this) {
        // cachedAdvisorBeanNames 是 advisor 名称的缓存
        advisorNames = this.cachedAdvisorBeanNames;
        /*
         * 如果 cachedAdvisorBeanNames 为空,这里到容器中查找,
         * 并设置缓存,后续直接使用缓存即可
         */ 
        if (advisorNames == null) {
            // 从容器中查找 Advisor 类型 bean 的名称
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            // 设置缓存
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<Advisor>();
    }

    List<Advisor> advisors = new LinkedList<Advisor>();
    // 遍历 advisorNames
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // 忽略正在创建中的 advisor bean
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    /*
                     * 调用 getBean 方法从容器中获取名称为 name 的 bean,
                     * 并将 bean 添加到 advisors 中
                     */ 
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Skipping advisor '" + name +
                                        "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }

    return advisors;

这个方法完成了两件事情:

  1. 从容器中查找所有类型为Advisor的bean对应的名称
  2. 遍历advisorNames,并从容器中获取对应的bean

3.2.1.2 buildAspectJAdvisors

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;

    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new LinkedList<Advisor>();
                aspectNames = new LinkedList<String>();
                // 从容器中获取所有 bean 的名称
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // 遍历 beanNames
                for (String beanName : beanNames) {
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    
                    // 根据 beanName 获取 bean 的类型
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }

                    // 检测 beanType 是否包含 Aspect 注解
                    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);

                            // 获取通知器
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        }
                        else {
                            if (this.beanFactory.isSingleton(beanName)) {
                                throw new IllegalArgumentException("Bean with name '" + beanName +
                                        "' is a singleton, but aspect instantiation model is not singleton");
                            }
                            MetadataAwareAspectInstanceFactory factory =
                                    new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                            this.aspectFactoryCache.put(beanName, factory);
                            advisors.addAll(this.advisorFactory.getAdvisors(factory));
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    //从缓存中取出放去advisors
    List<Advisor> advisors = new LinkedList<Advisor>();
    for (String aspectName : aspectNames) {
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    return advisors;
}

buildAspectJAdvisors这个方法的工作流程如下:

  1. 获取容器中所有bean的名称(beanName)
  2. 遍历上一步获取的bean名称数组,并获取当前beanName对应的bean类型(beanType)
  3. 根据beanType判断当前bean是否是一个Aspect注解类,若不是则不做任何处理
  4. 调用this.advisorFactory.getAdvisors(factory)获取通知器
  5. 放入缓存中,然后从缓存中取出,返回

advisorFactory.getAdvisors(factory),这个方法及它调用的方法完成的工作有,获得不含有@Pointcut注解的方法,对这个方法调用getAdvisor获得AspectJ表达式切点和Advisor实现类,这个实现类中有Advice类型。源码解析如下:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 获取 aspectClass 和 aspectName
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);

    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new LinkedList<Advisor>();

    // getAdvisorMethods 用于返回不包含 @Pointcut 注解的方法
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 为每个方法分别调用 getAdvisor 方法
        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.
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    // Find introduction fields.
    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    return advisors;
}

在getAdvisors中为每个不包含@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);
}

getAdvisor方法中包含两个步骤,一个是获取AspectJ表达式切点,另一个创建Advisor实现类。在第二个步骤中,包含一个隐藏步骤-创建Advice。

下面按照顺序依次分析这两个步骤,先看获取Aspect表达式切点(getPointcut)的过程,如下:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 获取方法上的 AspectJ 相关注解,包括 @Before,@After 等
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // 创建一个 AspectJExpressionPointcut 对象
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    // 设置切点表达式
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    ajexp.setBeanFactory(this.beanFactory);
    return ajexp;
}

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    // classesToLookFor 中的元素是大家熟悉的
    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;
}

要注意的是,在@After等中的表达式很有可能是@Pointcut抽取出来的表达式,也就是说ajexp设置的表达式只是一个中间表达式,不是最终值。后续要对这个表达式进行转换,这个转换过程现在就不分析了。

下面再来看看Advisor实现类的创建过程,如下:

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;

    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Pointcut preInstantiationPointcut = Pointcuts.union(
                aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

        this.pointcut = new PerTargetInstantiationModelPointcut(
                this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
        this.lazy = true;
    }
    else {
        this.pointcut = this.declaredPointcut;
        this.lazy = false;

        // 按照注解解析 Advice
        this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
}

上面是InstantiationModelAwarePointcutAdvisorImpl的构造方法,目前不关注这个方法中的一些初始化逻辑,现在来看构造方法中的最后一行代码,instantiateAdvice(this.declaredPointcut),这个方法用于创建通知Advice。通知器Advisor是通知Advice的持有者,所以在Advisor实现类的构造方法创建通知也是合适,下来看看构建通知的过程是怎样的,如下:

private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

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

    if (!isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" +
                candidateAspectClass.getName() + "]");
    }

    if (logger.isDebugEnabled()) {
        logger.debug("Found AspectJ method: " + candidateAdviceMethod);
    }

    AbstractAspectJAdvice springAdvice;

    // 按照注解类型生成相应的 Advice 实现类
    switch (aspectJAnnotation.getAnnotationType()) {
        case AtBefore:    // @Before -> AspectJMethodBeforeAdvice
            springAdvice = new AspectJMethodBeforeAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        case AtAfter:    // @After -> AspectJAfterAdvice
            springAdvice = new AspectJAfterAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        case AtAfterReturning:    // @AfterReturning -> AspectJAfterAdvice
            springAdvice = new AspectJAfterReturningAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;

        case AtAfterThrowing:    // @AfterThrowing -> AspectJAfterThrowingAdvice
            springAdvice = new AspectJAfterThrowingAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;

        case AtAround:    // @Around -> AspectJAroundAdvice
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        /*
         * 什么都不做,直接返回 null。从整个方法的调用栈来看,
         * 并不会出现注解类型为 AtPointcut 的情况
         */ 
        case AtPointcut:    
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
            
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }

    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    /*
     * 获取方法的参数列表名称,比如方法 int sum(int numX, int numY), 
     * getParameterNames(sum) 得到 argNames = [numX, numY]
     */
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 设置参数名
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();
    return springAdvice;
}

这个方法的主要逻辑就是根据注解类型生成与之对应的通知对象。

下面来总结下获取通知器getAdvisors整个过程的逻辑(进行了简化),如下:

  1. 从目标方法中获取不包含@Pointcut注解的方法列表
  2. 遍历上一步获取的方法列表,并调用getAdvisor获取当前方法对应的Advisor
  3. 创建AspectJExpressPointcut对象,并从方法中的注解获取表达式,最后设置到切点对象中
  4. 创建Advisor实现类对象InstantiationModelAwarePointcutAdvisorImpl
  5. 调用instantiateAdvice方法构建通知
  6. 调用getAdvice方法,并根据注解类型创建相应通知。

从上面方法中可以看到,Spring会根据不同的注解生成不同的增强器。下来我们一起来分析一下 AspectJMethodBeforeAdvice,也就是 @Before 注解对应的通知实现类。看看它的逻辑是什么样的。

3.2.1.3 AspectJMethodBeforeAdvice分析

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {

    public AspectJMethodBeforeAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }


    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // 调用通知方法
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }

    @Override
    public boolean isBeforeAdvice() {
        return true;
    }

    @Override
    public boolean isAfterAdvice() {
        return false;
    }

}

protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
    // 调用通知方法,并向其传递参数
    return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // 通过反射调用通知方法
        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();
    }
}

AspectJMethodBeforeAdvice 的源码比较简单,这里我们仅关注 before 方法。这个方法调用了父类中的 invokeAdviceMethod,然后 invokeAdviceMethod 在调用 invokeAdviceMethodWithGivenArgs,最后在 invokeAdviceMethodWithGivenArgs 通过反射执行通知方法。

剩下的实现,大家可以自己去看看。

3.2.2 筛选合适的通知器

前面函数已经完成了所有通知器的解析,但是对所有通知器来讲,并不一定都适用于当前bean,还要挑选出合适的通知器,也就是满足我们配置的通配符的通知器,具体实现下findAdvisorsThatCanApply中。

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        // 调用重载方法
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        // 筛选 IntroductionAdvisor 类型的通知器
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            continue;
        }

        // 筛选普通类型的通知器
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        /*
         * 从通知器中获取类型过滤器 ClassFilter,并调用 matchers 方法进行匹配。
         * ClassFilter 接口的实现类 AspectJExpressionPointcut 为例,该类的
         * 匹配工作由 AspectJ 表达式解析器负责,具体匹配细节这个就没法分析了,我
         * AspectJ 表达式的工作流程不是很熟
         */
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // 对于普通类型的通知器,这里继续调用重载方法进行筛选
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        return true;
    }
}

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 使用 ClassFilter 匹配 class
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        return true;
    }

    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    /*
     * 查找当前类及其父类(以及父类的父类等等)所实现的接口,由于接口中的方法是 public,
     * 所以当前类可以继承其父类,和父类的父类中所有的接口方法
     */ 
    Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {
        // 获取当前类的方法列表,包括从父类中继承的方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // 使用 methodMatcher 匹配方法,匹配成功即可立即返回
            if ((introductionAwareMethodMatcher != null &&
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

以上是通知器筛选的过程,筛选的工作主要由 ClassFilter 和 MethodMatcher 完成。关于 ClassFilter 和 MethodMatcher 我在上面核心组件解析一节中已经说过了,这里再说一遍吧。在 AOP 中,切点 Pointcut 是用来匹配连接点的,以 AspectJExpressionPointcut 类型的切点为例。该类型切点实现了ClassFilter 和 MethodMatcher 接口,匹配的工作则是由 AspectJ 表达式解析器复杂。

3.2.3 拓展筛选出通知器列表

protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // 如果通知器列表是一个空列表,则啥都不做
    if (!advisors.isEmpty()) {
        boolean foundAspectJAdvice = false;
        /*
         * 下面的 for 循环用于检测 advisors 列表中是否存在 
         * AspectJ 类型的 Advisor 或 Advice
         */
        for (Advisor advisor : advisors) {
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true;
            }
        }

        /*
         * 向 advisors 列表的首部添加 DefaultPointcutAdvisor,
         * 至于为什么这样做,我会在后续的文章中进行说明
         */
        if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true;
        }
    }
    return false;
}

private static boolean isAspectJAdvice(Advisor advisor) {
    return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
            advisor.getAdvice() instanceof AbstractAspectJAdvice ||
            (advisor instanceof PointcutAdvisor &&
                     ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}

由源码可以看出 extendAdvisors 是一个空壳方法,除了调用makeAdvisorChainAspectJCapableIfNecessary,该方法没有其他更多的逻辑了。至于 makeAdvisorChainAspectJCapableIfNecessary 这个方法,该方法主要的目的是向通知器列表首部添加 DefaultPointcutAdvisor 类型的通知器,也就是 ExposeInvocationInterceptor.ADVISOR。至于添加此种类型通知器的意图,我会在后面文章里说明。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值