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

33--SpringAop获取增强(一)

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

上一节分析了aspectj-autoproxy标签的解析过程,并注册了AnnotationAwareAspectJAutoProxyCreator。但是该类的作用是什么呢,看起来茫然无措,这时不妨查看一下类的继承关系结构。

AnnotationAwareAspectJAutoProxyCreator类图结构

从上图可以看到,AnnotationAwareAspectJAutoProxyCreator类实现了BeanPostProcessor接口,读过之前的章节或者对bean的生命周期有所了解的同学一定知道,在bean实例化完成之前和完成之后分别会自动BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization方法。

依次打开AnnotationAwareAspectJAutoProxyCreator的父类,在AbstractAutoProxyCreator类里,找到postProcessBeforeInitialization方法和postProcessAfterInitialization方法,开始分析。

1. postProcessBeforeInstantiation方法
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    // 1、预处理判断
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // 判断该类是否应被处理过
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // 判断beanClass是否需要被代理
        // isInfrastructureClass-->判断beanClass是否为AOP基础类例如Advice(增强),Advisors(切面),Pointcut(切点)
        // shouldSkip-->判断beanClass是否指定了不需要代理
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            // 缓存找到的切面类信息
            this.advisedBeans.put(cacheKey, Boolean.FALSE); 
            return null;
        }
    }

    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    // 2、如果有自定义TargetSource的话,则在此创建代理
    /**
     *  自定义TargetSource示例:
     *  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
     *      <property name="customTargetSourceCreators">
     *          <list>
     *              <bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator"/>        
     *          </list>
     *      </property>
     *  </bean>
     */
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    return null;
}

postProcessBeforeInstantiation一共做了两件事情

  1. 缓存切面类的信息
    先判断对应的beanClass是否为Aop的基础类,如果是的话,直接缓存。
    然后通过shouldSkip方法判断beanClass是否需要被自动代理,如果不需要被自动代理的话,返回true,否则返回false。
  2. 判断有无自定义TargetSource,如果有的话,则在此方法里创建代理
    关于自定义TargetSource已经给出了一个简单的实例,感兴趣的同学可以自己debug代码。

其中第一步的shouldSkip(beanClass, beanName)方法,看似简单,其实这里面包含了非常复杂的业务逻辑处理。·即增强的提取

2. shouldSkip方法简析
/**
 * 如果给定的bean不应该被认为是后处理器自动代理的,子类应该重写这个方法以返回true。
 * @param beanClass the class of the bean
 * @param beanName the name of the bean
 * @return
 */
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    // 1、查找所有候选增强
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2、循环判断所有的增强,如果增强是AspectJPointcutAdvisor的实例
    //    并且其名称与当前bean的名称相同,则返回true,即该bean无需代理
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor
                && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

该方法主逻辑很简单,查找所有的候选增强并循环判断当前beanName是否需要被代理,关键是findCandidateAdvisors()方法中获取增强的过程。

/**
 * AnnotationAwareAspectJAutoProxyCreator
 * 查找所有候选增强方法
 * @return
 */
@Override
protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    // 1、从父类中获取所有的增强
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    // 2、从当前BeanFactory中查找所有标记了@AspectJ的注解的bean,并返回增强注解集合
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}

该方法也非常简单:

  • 从父类中获取所有的增强
  • 从当前BeanFactory中查找所有标记了@AspectJ的注解的bean,并返回增强注解集合
    逐个对其进行分析:
3. findCandidateAdvisors方法分析
protected List<Advisor> findCandidateAdvisors() {
    return this.advisorRetrievalHelper.findAdvisorBeans();
}
/**
 * 从当前BeanFactory中寻找所有的增强bean,忽略FactoryBean和正在创建的bean
 * Find all eligible Advisor beans in the current bean factory,
 * ignoring FactoryBeans and excluding beans that are currently in creation.
 * @return the list of {@link org.springframework.aop.Advisor} beans
 * @see #isEligibleBean
 */
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    // 获取缓存的增强
    String[] advisorNames = this.cachedAdvisorBeanNames;
    // 缓存增强为空,则重新查找增强并缓存
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        // 从当前BeanFactory中获取所有类型为Advisor的bean
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory,
                Advisor.class,
                true,
                false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    // 当前BeanFactory中没有类型为Advisor的bean则返回一个空的集合
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    // 循环所有获取到的bean
    List<Advisor> advisors = new ArrayList<>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // 跳过正在创建的增强
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    // 通过getBean方法获取bean实例
                    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.isDebugEnabled()) {
                                logger.debug("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;
}
4. buildAspectJAdvisors方法分析
/**
 * 在当前BeanFactory中查找AspectJ-annotated的注解信息,并返回增强集合
 * Look for AspectJ-annotated aspect beans in the current bean factory,
 * and return to a list of Spring AOP Advisors representing them.
 * <p>Creates a Spring Advisor for each AspectJ advice method.
 * @return the list of {@link org.springframework.aop.Advisor} beans
 * @see #isEligibleBean
 */
public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;

    // 1、提取增强
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                // 1.1、获取容器中的所有bean
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // 1.2、循环所有的bean,并从切面类上提取增强
                for (String beanName : beanNames) {

                    // 判断该beanName是否符合条件
                    // 如果配置文件指定了<aop:include/>属性,那么只有符合表达式条件的切面类的增强才会被提取
                    // 例如:配置<aop:include name="dogAspect"></aop:include>
                    // 那么只有dogAspect切面类的增强才会被提取
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    // We must be careful not to instantiate beans eagerly as in this case they
                    // would be cached by the Spring container but would not have been weaved.
                    // 获取beanType类型
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    // 如果beanType是一个切面类
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        /**
                         * 切面实例化模型简介
                         *
                         * singleton: 即切面只会有一个实例;
                         * perthis  : 每个切入点表达式匹配的连接点对应的AOP对象都会创建一个新切面实例;
                         *            使用@Aspect("perthis(切入点表达式)")指定切入点表达式;
                         *            例如: @Aspect("perthis(this(com.lyc.cn.v2.day04.aspectj.Dog))")
                         * pertarget: 每个切入点表达式匹配的连接点对应的目标对象都会创建一个新的切面实例;
                         *            使用@Aspect("pertarget(切入点表达式)")指定切入点表达式;
                         *            例如:
                         *
                         * 默认是singleton实例化模型,Schema风格只支持singleton实例化模型,而@AspectJ风格支持这三种实例化模型。
                         */
                        // singleton实例化模型处理
                        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);
                        }
                        // perthis或pertarget实例化模型处理
                        else {
                            /**
                             * 注意:当使用perthis或pertarget属性时,切面类不能是单例bean,否则会抛出下面的异常
                             * 例如:<bean name="dogAspect" class="com.lyc.cn.v2.day04.aspectj.DogAspect" scope="singleton"/>
                             * 则会报错,应该为
                             *      <bean name="dogAspect" class="com.lyc.cn.v2.day04.aspectj.DogAspect" scope="prototype"/>
                             */
                            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;
            }
        }
    }

    // 2、返回从缓存中获取提取到的增强方法
    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    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;
}

其中的步骤在代码里都标注的很清晰了,这里会涉及到一个切面实例化模型的概念,如果初看AOP的源码,完全可以忽略该概念,把精力放在主线的分析上。其实该方法里大部分都是预处理、缓存、以及各种业务判断,增强的提取工作交给了List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);。这也是我们要分析的重点。

相关文章

  • 33--SpringAop获取增强(一)

    引 上一节分析了aspectj-autoproxy标签的解析过程,并注册了AnnotationAwareAspec...

  • Spring(5)- (16)AOP各种增强细节

    (一) 各种增强 (二) 增强参数 1.在增强方法中获取异常信息 2.获取被增强方法信息,并传递给增强方法。 (1...

  • Spring源码-AOP(七)-增强器的获取

    前言 前面一章简单说明了AOP代理的创建,接下来开始详细的分析其内部实现,分析增强器的获取 增强器的获取 在上一篇...

  • 指数增强型基金

    指数增强型基金是在被动的指数化投资的基础上,加入一定的主动投资策略,进行适当地“增强”。所以指数增强型基金能在获取...

  • 十四、spring aop之创建代理

    接上一节十三、spring aop之查找匹配的增强器,在获取所有对应bean的增强器后,便可以进行代理的创建。 初...

  • 读论文Sim-to-Real Robot Learning fr

    我们知道,无论是深度学习还是深度增强学习,都是一种表示经验的办法,都需要通过样本获取和表达经验。但是,深度增强学习...

  • 34--SpringAop获取增强(二)

    引 在上一篇结尾,我们得到了增强的提取工作交给了List classAdvisors = this.adviso...

  • 《终身成长》—4/7—1组—裘叔—Queena群

    本期主题:从失败获取信息(增强信心方法) R 在当今社会中,对于如何增强孩子的自信心,存在着一种强有力的信息,...

  • iOS个人信息获取说明

    iOS个人信息获取说明 需要授权获取的列表有iOS 10以后,苹果增强了对用户隐私的保护,访问照相机、相册、麦克风...

  • 阅读的活力

    阅读是一种通过语言文字来获取资讯,以增强对世界了解的行为活动,是从视觉材料中获取信息。 主动的阅读 通常认为,写和...

网友评论

    本文标题:33--SpringAop获取增强(一)

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