spring 5.0.x源码学习系列八: 实例化bean之使用构造方法创建bean、自动装配与循环依赖

前言

一、解析构建bean过程

  • 构建bean的过程太复杂,将根据我自己写的流程图来一一讲解

1.1 流程图

在这里插入图片描述

1.2 流程图解析

1.2.1 创建非抽象、单例、非懒加载的bean

整个正常流程是遍历bean工厂存放beanName的list, 并根据beanName拿到对应的beanDefinition,只过滤掉非抽象单例非懒加载的beand进行创建

1.2.2 判断当前创建bean的类型并作相应处理

因为spring存在两种bean, 普通beanFactoryBean,spring在区分这两种bean的做法是beanName和beanType来双重校验的。假设获取的bean为FactoryBean,那么会在getBean之前在beanName前添加一个&符号

    if (isFactoryBean(beanName)) {
        // org.springframework.beans.factory.BeanFactory#FACTORY_BEAN_PREFIX
        // String FACTORY_BEAN_PREFIX = "&";
        Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
        // 后面的逻辑省略
    }

1.2.3 getBean <=> doGetBean获取bean的通用方法

  • 此方法是spring根据beanName获取bean的通用方法。在此方法中有两个地方需要注意:
  1. 内部会对传入的beanName做处理(将传入的beanName前面的&符号去掉并存入内部自己维护的一个变量中), 此时关于当前bean的名称会有两个变量存储,分别是name(方法传入的bean名称), beanName(处理过bean名称)
  2. 不管是从bean工厂的单例池中获取还是新建的bean, 最终都要走getObjectForBeanInstance方法来返回bean。这样做主要是为了处理获取的bean类型为FactoryBean的情况。
    具体规则可参考此链接:https://github.com/AvengerEug/spring/tree/develop/resourcecode-study#%E5%8D%81%E4%B8%80-%E8%8E%B7%E5%8F%96factorybean%E5%AE%9E%E4%BE%8B%E4%B8%8E%E5%AE%83%E7%BB%B4%E6%8A%A4%E7%9A%84bean%E5%AE%9E%E4%BE%8B%E8%A7%84%E5%88%99

1.2.4 第一次getSingleon

  • 此方法很重要
  1. 正常逻辑(循环bean工厂挨个创建bean)进来, 这里获取的值肯定为null
  2. 在populateBean自动装配需要创建依赖bean时, 这里获取的可能为null也可能不为null. 只有在循环依赖和spring单例池存在bean的情况下, 获取的才不为null, 因为此方法会首先从单例池中获取bean, 若不存在再根据这个bean是否正在被创建从而到"特殊"(根据beanName到singletonFactories拿ObjectFactory, 并根据ObjectFactory获取bean。
    public Object getSingleton(String beanName)
    

1.2.5 第二次调用getSingleton

  • 此方法只有在创建新的bean时才会用到,传入的ObjectFactory是当前类的createBean方法
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)
    

1.2.6 getSingleton内部的beforeSingletonCreation方法

  • 标记bean正在被创建:此动作很重要, 主要是将beanName存放至DefaultSingletonBeanRegistry类的singletonsCurrentlyInCreation数据结构(Set<String>), 后续会根据这个依据来解决循环依赖的情况, 即上述1.2.4 第一次getSingleon中所说的获取不为null的情况
    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }
    

1.2.7 调用传入的createBean方法<=>singletonFactory.getObject()

  • 内部会调用至doCreateBean方法

1.2.8 doCreateBean方法

  • 内部会创建出来bean, 创建完后还会在此方法中进行自动装配populateBean

1.2.9 createBeanInstance方法

  • 此方法为真正创建bean的流程,内部主要做了如下事情
  1. 确定当前bean是否通过构造方法自动装配, 源码和条件如下:

    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
       if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
               mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
           return autowireConstructor(beanName, mbd, ctors, args);
       }
    
    1. ctors != null  => ctors中存的是构造方法中添加@Autowired注解的情形
    2. mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR  => 此条件为使用后置处理器手动修改beanDefinition的自动装配的值, 默认为no
    3. mbd.hasConstructorArgumentValues()
        => 使用后置处理器手动添加beanDefinition中存放构造方法值的情形,
         eg: mybatis中的definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
    4. !ObjectUtils.isEmpty(args)
    
    上述条件只要满足一个就走构造方法自动装配的逻辑
        ```
    
  2. 若使用构造方法自动装配的结论不成立则走默认构造方法创建bean流程

  3. 不管是默认构造方法创建bean还是通过构造方法自动装配bean,所有bean的创建使用的都是CglibSubclassingInstantiationStrategy策略

1.2.10 确认当前bean是否符合循环依赖的情况 <=> addSingletonFactory方法

  • 满足循环依赖的三个条件: 单例、allowCircularReferences为true、当前的bean正在创建中
        protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(singletonFactory, "Singleton factory must not be null");
            synchronized (this.singletonObjects) {
                if (!this.singletonObjects.containsKey(beanName)) {
                    // 存放bean对应的ObjectFactory, 在bean的循环依赖中会用上
                    this.singletonFactories.put(beanName, singletonFactory);
                    this.earlySingletonObjects.remove(beanName);
                    // 存放已经被注册过的bean
                    this.registeredSingletons.add(beanName);
                }
            }
        }
    
  • 使用构造方法创建bean后, 会做三个操作:
  1. this.singletonFactories.put(beanName, singletonFactory);
    => 在getBean方法的第一次getSingleton中会用到,其实这个singletonFactory的形参中已经在创建bean的过程中传入了,并且内部的逻辑是执行SmartInstantiationAwareBeanPostProcessor后置处理器的getEarlyBeanReference方法,此方法可以返回一个bean)的地方去拿bean

  2. this.earlySingletonObjects.remove(beanName);

  3. this.registeredSingletons.add(beanName); => 此步骤只是为了记录这个bean已经被spring创建了或者正在被创建中

    所以创建一个bean后(还未依赖注入bean), 会在singletonFactories和registeredSingletons数据结构中都存在相关记录

1.2.11 自动装配 <=> populateBean方法

  • 此方法中主要是AutowiredAnnotationBeanPostProcessor后置处理器来实现自动装配,若bean需要自动装配,则还需要先实例化依赖的bean,最终又会走到上述getBean逻辑

1.2.12 bean创建完毕, 开始执行bean的一些后置处理器

  1. 调用bean的BeanPostProcessor后置处理器postProcessBeforeInitialization方法
  2. 若bean实现了InitializingBean接口, 则调用接口中的方法afterPropertiesSet
  3. 调用bean的BeanPostProcessor后置处理器de postProcessAfterInitialization方法

1.2.13 清除bean正在创建的标识并添加至bean工厂

  • afterSingletonCreation
    // this.singletonsCurrentlyInCreation.remove(beanName) => 移除当前bean name
    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }
  • addSingleton
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // bean工厂单例池
            this.singletonObjects.put(beanName, singletonObject);
            // 执行到这表示当前bean已经创建完成, 则移除循环依赖的bean的情况, 
            // 若下次还有bean依赖它, 直接从bean工厂的单例池获取即可
            this.singletonFactories.remove(beanName);

            // 当前bean被创建完毕,则清除它
            this.earlySingletonObjects.remove(beanName);

            // 再次将bean添加至该集合中, 该集合是一个set, 不会重复, 这里重复添加的原因可能是并发情况
            this.registeredSingletons.add(beanName);
        }
    }

二、一个案例搞清楚循环依赖

2.1 案例背景

  • 有两个类: A和B,它们互相依赖(A中要自动装配B属性,B中要自动装配A属性), 假设先创建A

2.2 流程

  1. 创建Bean a <=> getBean("a")
    第一次调用getSingleton方法, 单例池中无bean a ,也没标识正在被创建, 所以走第二个getSingleton方法。此时a被标注正在被创建走使用构造方法创建bean的逻辑
  2. 在创建完a后开始记录bean a的一些状态(将bean name添加至registeredSingletons以及将bean对应的ObjectFactory添加至singletonFactories。即上述的addSingletonFactory方法的操作)和填充属性, 发现它依赖于B, 此时要去getBean(b)
  3. 然后进入创建bean b流程 <=> getBean("b")
  4. 此时会进入第一个getSingleton方法, 此时spring单例池中无b并且b也还未被标注为被创建(因为它是在getSingleton方法后面标注的)状态所以进入创建createBean方法
  5. 同理, 在创建完b后也会记录bean b的一些状态(将bean name添加至registeredSingletons以及将bean对应的ObjectFactory添加至singletonFactories。即上述的addSingletonFactory方法的操作)
  6. 创建完b后开始填充属性, 发现它又依赖于A, 此时要去getBean("a")
  7. 此时进入第一个getSingleton方法, 发现单例池中无a(虽然a在上述过程中被创建了, 但是它还未放入单例池中)但是它是处于被创建的状态, 所以从singletonFactories中根据beanName拿ObjectFactory, 最终从ObjectFactory拿到Bean a, 并将singletonFactories中beanName对应的objectFactory remove掉,以及将拿到的bean a放入到earlySingletonObjects中
  8. 最终拿到了bean a, 此时将bean a注入到bean b中的a属性中, 完成bean b的创建(此时会将它正在创建的标识移除并它放入单例池中去), 并调用BeanPostProcessor后置处理器以及InitializingBean接口的afterPropertiesSet方法, 至此bean b的创建结束了 返回bean b。
  9. 因为上述创建bean b的过程是a要自动装配b而执行的, 为的就是获取bean b。现在已经拿到bean b了, 所以将bean a中的b属性给注入进去, 最终完成bean a的创建, 将a也添加到spring单例池中并调用BeanPostProcessor后置处理器以及InitializingBean接口的afterPropertiesSet方法
  10. bean a创建完成,循环依赖步骤完成
  • 涉及到的几个数据结构:
    1. registeredSingletons: 表示该bean已经通过构造方法创建出来了
    2. singletonFactories: 存放bean对应的objectFactory方法, 循环依赖时需要根据它来拿bean
    3. earlySingletonObjects: 与singletonFactories的操作是互斥的, 里面存放的是singletonFactories中beanName对应的objectFactory创建出来的bean,若一个beanName在该集合中存在, 那么该bean对应的ObjectFactory就会在singletonFactories中被remove掉
    4. singletonsCurrentlyInCreation: 表示当前bean正在被创建, 在getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法中进行操作, 添加、移除正在创建的标识都是在此方法中完成

三、spring bean实例化过程中涉及到的后置处理器和执行顺序

执行顺序 执行位置 执行到的后置处理器 执行的方法 作用
1 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.doCreateBean
=> resolveBeforeInstantiation方法
InstantiationAwareBeanPostProcessor 1. postProcessBeforeInstantiation
2.postProcessAfterInitialization
1. postProcessBeforeInstantiation作用:
若后置处理器中返回了一个对象, 则不会走spring的创建bean的流程
2.若postProcessBeforeInstantiation方法返回的bean不为null则执行postProcessAfterInitialization方法,该方法执行了BeanPostProcessor后置处理器, 自动装配的后置处理器就间接实现了BeanPostProcessor, 若BeanPostProcessor不被执行, 那么自动装配的功能也将缺失
3. 若bean中实现了aware接口,那么将按照aware的类型诶个执行对应的方法,其中就包括获取spring上下文的ApplicationContextAware
2 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.createBeanInstance
=> determineConstructorsFromBeanPostProcessors
SmartInstantiationAwareBeanPostProcessor determineCandidateConstructors 扫描当前bean携带@Autowired注解的构造方法或者只有一个带参的构造方法或者等等其他的情况。(具体参考AutowiredAnnotationBeanPostProcessor类的determineCandidateConstructors方法)
3 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.doCreateBean
=> applyMergedBeanDefinitionPostProcessors
MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition 将当前类需要自动装配的属性全部InjectionMetadata对象中(每个属性对应一个InjectionMetadata对象)
4 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.doCreateBean
=> getEarlyBeanReference
SmartInstantiationAwareBeanPostProcessor getEarlyBeanReference 当处理循环依赖时,会获取到状态为'正在创建'的bean的引用
5 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.populateBean
=> postProcessAfterInstantiation
InstantiationAwareBeanPostProcessor postProcessAfterInstantiation 能够控制当前bean是否继续完成依赖注入逻辑,若方法return false,则当前bean的@Autowired注解功能的依赖注入结束
6 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.populateBean
=> postProcessPropertyValues
InstantiationAwareBeanPostProcessor postProcessPropertyValues 处理当前bean自动装配的属性
7 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.initializeBean => applyBeanPostProcessorsBeforeInitialization
BeanPostProcessor postProcessBeforeInitialization 当bean被实例化并完成自动装配之后执行
8 org.springframework.beans.factory.support.
AbstractAutowireCapableBeanFactory.initializeBean
=> applyBeanPostProcessorsAfterInitialization
BeanPostProcessor postProcessAfterInitialization 当bean被实例化并完成自动装配之后执行,比postProcessBeforeInitialization方法后执行
  • 如上, 一共会执行4个后置处理器InstantiationAwareBeanPostProcessor, SmartInstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, BeanPostProcessor, 共执行了8次, 其中InstantiationAwareBeanPostProcessor类型的后置处理器调用次数最多, 8次调用中有5次跟它有关, 因为它继承了BeanPostProcessor, 并扩展了三个方法postProcessBeforeInstantiation、 postProcessAfterInstantiation和postProcessPropertyValues

四、小结

  • 本篇博客还有很多细节未提及,比如spring如何知道使用哪一个构造方法(假设程序员提供了多个构造器)创建bean、自动装配后置处理器AutowiredAnnotationBeanPostProcessor是如何知道哪些属性需要装配哪些不需要、在5个后置处理器的八个地方被调用的具体作用和景点案例也未完全总结等等。只能等日后再完善了
  • I am a slow walker, but I never walk backwards.
  • github spring源码学习地址: https://github.com/AvengerEug/spring/tree/develop/resourcecode-study
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容