33--SpringAop获取增强(一)

上一节分析了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);。这也是我们要分析的重点。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容