spring-1

spring-1

循环依赖

循环依赖在创建bean的过程中会存在什么问题?

  1. 通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。
  2. A对象需要注入B对象,发现对象池(缓存)里还没有B对象(对象在创建并且注入属性和初始化完成之后,会放入对象缓存里)。
  3. 通过构建函数创建B对象(B对象是半成品,还没注入属性和调用init方法)。
  4. B对象需要注入A对象,发现对象池里还没有A对象。
  5. 创建A对象,循环以上步骤。

三级缓存

Spring解决循环依赖的核心思想在于提前曝光:

  1. 通过构建函数创建A对象(A对象是半成品,还没注入属性和调用init方法)。
  2. A对象需要注入B对象,发现缓存里还没有B对象,将半成品对象A放入半成品缓存。
  3. 通过构建函数创建B对象(B对象是半成品,还没注入属性和调用init方法)。
  4. B对象需要注入A对象,从半成品缓存里取到半成品对象A。
  5. B对象继续注入其他属性和初始化,之后将完成品B对象放入完成品缓存。
  6. A对象继续注入属性,从完成品缓存中取到完成品B对象并注入。
  7. A对象继续注入其他属性和初始化,之后将完成品A对象放入完成品缓存。
/** Cache of singleton objects: bean name to bean instance. */
/** 第一级缓存,存放可用的成品Bean。*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */
/** 第二级缓存,存放半成品的Bean,半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);


/** Cache of singleton factories: bean name to ObjectFactory. */
/** 第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决循环依赖。 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

doCreateBean

  1. 在构造Bean对象之后,将对象提前曝光到缓存中,这时候曝光的对象仅仅是构造完成,还没注入属性和初始化。
  2. 提前曝光的对象被放入Map<String, ObjectFactory<?>> singletonFactories缓存中,这里并不是直接将Bean放入缓存,而是包装成ObjectFactory对象再放入。

为什么要包装一层ObjectFactory对象?

如果创建的Bean有对应的代理,那其他对象注入时,注入的应该是对应的代理对象;但是Spring无法提前知道这个对象是不是有循环依赖的情况,而正常情况下(没有循环依赖情况),Spring都是在创建好完成品Bean之后才创建对应的代理。这时候Spring有两个选择:

  1. 不管有没有循环依赖,都提前创建好代理对象,并将代理对象放入缓存,出现循环依赖时,其他对象直接就可以取到代理对象并注入。
  2. 不提前创建好代理对象,在出现循环依赖被其他对象注入时,才实时生成代理对象。这样在没有循环依赖的情况下,Bean就可以按着Spring设计原则的步骤来创建。

Spring选择了第二种方式,那怎么做到提前曝光对象而又不生成代理呢?

Spring就是在对象外面包一层ObjectFactory,提前曝光的是ObjectFactory对象,在被注入时才在ObjectFactory.getObject方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects.addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

为了防止对象在后面的初始化(init)时重复代理,在创建代理时,earlyProxyReferences缓存会记录已代理的对象。

注入属性和初始化

提前曝光之后:

  1. 通过populateBean方法注入属性,在注入其他Bean对象时,会先去缓存里取,如果缓存没有,就创建该对象并注入。
  2. 通过initializeBean方法初始化对象,包含创建代理。

放入已完成创建的单例缓存

在经历了以下步骤之后,最终通过addSingleton方法将最终生成的可用的Bean放入到单例缓存里。

  1. AbstractBeanFactory.doGetBean ->
  2. DefaultSingletonBeanRegistry.getSingleton ->
  3. AbstractAutowireCapableBeanFactory.createBean ->
  4. AbstractAutowireCapableBeanFactory.doCreateBean ->
  5. DefaultSingletonBeanRegistry.addSingleton

有代理的情况下的创建顺序

  1. A半成品加入第三级缓存
  2. A填充属性注入B -> 创建B对象 -> B半成品加入第三级缓存
  3. B填充属性注入A -> 创建A代理对象,从第三级缓存移除A对象,A代理对象加入第二级缓存(此时A还是半成品,B注入的是A代理对象)
  4. 创建B代理对象(此时B是完成品) -> 从第三级缓存移除B对象,B代理对象加入第一级缓存
  5. A半成品注入B代理对象
  6. 从第二级缓存移除A代理对象,A代理对象加入第一级缓存
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容