首页 > 编程语言 > 详细

Spring AOP 源码分析 - 筛选合适的通知器

时间:2020-04-16 19:19:35      阅读:58      评论:0      收藏:0      [点我收藏+]

1. AOP 入口分析


 

  我已经说过 Spring AOP 是在何处向目标 bean 中织入通知(Advice)的。也说过 Spring 是如何将 AOP 和 IOC 模块整合到一起的,即通过拓展点 BeanPostProcessor 接口。Spring AOP 抽象代理创建器实现了 BeanPostProcessor 接口,并在 bean 初始化后置处理过程中向 bean 中织入通知。下面我们就来看看相关源码,如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            //如果之前调用过getEarlyBeanReference获取包装目标对象到AOP代理对象(如果需要),则不再执行
            //getEarlyBeanReference 也是创建代理对象, 主要是通过代理来解决循环引用问题
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                //// 如果需要,为 bean 生成代理对象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && 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;
        }

        // Create proxy if we have advice. //注释说的很明白了,如果偶们有advice就创建代理
        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);
        return bean;
    }

以上就是 Spring AOP 创建代理对象的入口方法分析,过程比较简单,这里简单总结一下:

  1. 若 bean 是 AOP 基础设施类型,则直接返回
  2. 为 bean 查找合适的通知器
  3. 如果通知器数组不为空,则为 bean 生成代理对象,并返回该对象
  4. 若数组为空,则返回原始 bean

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

  如上,Spring 先查询出所有的通知器,然后再调用 findAdvisorsThatCanApply 对通知器进行筛选。在下面几节中,我将分别对 findCandidateAdvisors 和 findAdvisorsThatCanApply 两个方法进行分析,继续往下看吧。

2.1 findCandidateAdvisors 方法分析

    protected List<Advisor> findCandidateAdvisors() {
        Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

  从上面的源码中可以看出,AbstractAdvisorAutoProxyCreator 中的 findCandidateAdvisors 是个空壳方法,所有逻辑封装在了一个 BeanFactoryAdvisorRetrievalHelper 的 findAdvisorBeans 方法中。这里大家可以仔细看一下类名 BeanFactoryAdvisorRetrievalHelper 和方法 findAdvisorBeans,两个名字其实已经描述出他们的职责了。BeanFactoryAdvisorRetrievalHelper 可以理解为从 bean 容器中获取 Advisor 的帮助类,findAdvisorBeans 则可理解为查找 Advisor 类型的 bean。所以即使不看 findAdvisorBeans 方法的源码,我们也可从方法名上推断出它要做什么,即从 bean 容器中将 Advisor 类型的 bean 查找出来。下面我来分析一下这个方法的源码,如下:

public List<Advisor> findAdvisorBeans() {
        // cachedAdvisorBeanNames 是 advisor 名称的缓存
        String[] 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 ArrayList<>();
        }

        List<Advisor> advisors = new ArrayList<>();
        // 遍历 advisorNames
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                // 忽略正在创建中的 advisor bean
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("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;
                            String bceBeanName = bce.getBeanName();
                            if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                if (logger.isTraceEnabled()) {
                                    logger.trace("Skipping advisor ‘" + name +
                                            "‘ with dependency on currently created bean: " + ex.getMessage());
                                }
                                // Ignore: indicates a reference back to the bean we‘re trying to advise.
                                // We want to find advisors other than the currently created bean itself.
                                continue;
                            }
                        }
                        throw ex;
                    }
                }
            }
        }
        return advisors;
    }

  以上就是从容器中查找 Advisor 类型的 bean 所有的逻辑,代码虽然有点长,但并不复杂。主要做了两件事情:

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

2.2 筛选合适的通知器

  查找出所有的通知器,整个流程还没算完,接下来我们还要对这些通知器进行筛选。适合应用在当前 bean 上的通知器留下,不适合的就让它自生自灭吧。那下面我们来分析一下通知器筛选的过程,如下:

 

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 完成。在 AOP 中,切点 Pointcut 是用来匹配连接点的,以 AspectJExpressionPointcut 类型的切点为例。该类型切点实现了ClassFilter 和 MethodMatcher 接口,匹配的工作则是由 AspectJ 表达式解析器复杂。除了使用 AspectJ 表达式进行匹配,Spring 还提供了基于正则表达式的切点类,以及更简单的根据方法名进行匹配的切点类。大家有兴趣的话,可以自己去了解一下,这里就不多说了。

在完成通知器的查找和筛选过程后,还需要进行最后一步处理 – 对通知器列表进行拓展。怎么拓展呢?我们一起到下一节中一探究竟吧。

 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。至于添加此种类型通知器的意图,我会在后面文章里说明,这里不便展开。关于 extendAdvisors 这个方法,这里就先说到这了。

 

Spring AOP 源码分析 - 筛选合适的通知器

原文:https://www.cnblogs.com/w2116o2115/p/12714732.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!