关于spring:spring-ioc下循环依赖三级缓存和FactoryBean

上一篇文章,整体介绍了spring ioc容器初始化过程(starter->refresh->registerBeanDefinition->doCreateBean->populateBean)

但仅限于惯例流程。一些非凡状况,比方“对象循环利用”spring会如何解决——这是本篇要解答的问题。

1.spring如何解决对象的循环依赖问题?

留神:
构造函数型循环依赖,spring会间接抛异样
这里关注的是属性注入的循环依赖

什么是循环依赖?

@Component
class A{
    @Autowire
    B b;
}

@Component
class B{
    @Autowire
    A a;
}

下面的代码就是比较简单的循环依赖:A援用B的同时,B也援用了A

这种状况初始化时会遇到什么问题?
创立A的时候,发现援用了B,转而去创立B;创立B的时候,又发现援用了A,转而去创立A产生了死循环……

怎么解决?

解决形式也简略,只有申明一个汇合(如:creatingSet),须要创建对象时先去查看汇合中有没有,没有再创立,并把对象放入汇合中。

这样,A、B对象的初始化过程就变成了:

  1. 查看creatingSet中有没有对象A,没有则创立A,并记录下来creatingSet.set(A)
  2. 发现A援用了B转而创立B,creatingSet中没有对象B,创立并记录creatingSet.set(B)
  3. 发现B援用A,此时creatingSet中存在对象A,不用再创立间接赋值

spring也是这个思路,它是通过singletonsCurrentlyInCreation汇合记录正在被创立的对象名,而用三级缓存寄存对象。

spring怎么解决?

用一种更简略的循环依赖状况举例:

@Component
class A{
    @Autowire
    A a1;
}

咱们来看看A对象的初始化流程

getBean(beanName){
    // -- 1.减少标记【singletonsCurrentlyInCreation.set(beanName)】
    beforeSingletonCreation(beanName);

    // -- 2.构造函数创建对象【A对象创立了,它的属性a1为空】
    instanceWrapper = createBeanInstance(beanName, mbd, args);

    // -- 3.放入三级缓存,移除二级【三级缓存中存在a1的工厂】
    // ## 只有循环依赖才会执行到此,代理动作提前了
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    // -- 4.属性初始化,递归调用getSingleton(String,boolean)
    // 【初始化属性a1 开始】
    populateBean();
       ⬇⬇⬇⬇⬇
    Object getSingleton(String beanName, boolean allowEarlyReference)
      Object getSingleton(String beanName, boolean allowEarlyReference) {
       // == a.尝试从一级缓存获取
       Object singletonObject = this.singletonObjects.get(beanName);
       // == b.一级缓存没有 && 但正在创立(递归时会进入if)
       if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
           // ** 通过三级缓存获取到的工厂创立
           singletonObject = singletonFactory.getObject();
           // 退出二级,移除三级
           this.earlySingletonObjects.put(beanName, singletonObject);
           this.singletonFactories.remove(beanName);
       }
       // == c.递归时,执行完`代码2`的if,返回二级缓存对象
       return singletonObject;
    }
      ⬆⬆⬆⬆⬆
    // 【初始化属性a1 完结:二级缓存中存在a1对象,三级缓存的a1工厂移除】
    populateBean(); 

    // ## 4.5 调用对象后置处理器【代码3曾经提前做了代理,此处不执行】
    // ## initializeBean(beanName, exposedObject, mbd)

    // -- 5.对属性进行注入【A对象的a1属性有值】
    applyPropertyValues(beanName, mbd, bw, pvs);

    // -- 6.移除标记【singletonsCurrentlyInCreation.remove(beanName)】
    afterSingletonCreation(beanName);

    // -- 7.移除三级、移除二级,对象存入一级缓存【完结撒花,A对象初始化实现】
    addSingleton(beanName, singletonObject);

代码3代码4代码4.5三处产生了变动。

以上就是spring解决对象循环利用问题的形式。

2.spring为什么设计了二级、三级这两级缓存解决循环依赖?

这个问题其实又能够分为两个问题。

A.只用二级缓存:代码3地位间接创立代理对象是否可行?

答:能够,但不合乎spring理念

spring的理念是:Bean在生命周期的最初一步实现代理,而不是实例化之后立马实现代理。

总的来说,解决循环依赖有两种思路:

  1. 不论有没有循环依赖,提前创立好代理对象,将代理对象放入缓存;等呈现循环依赖时,间接从缓存获取。
    本节探讨的问题——只用二级缓存:代码3地位间接创立代理对象——就是这种形式。
    甚至再糙一点,二级缓存也省了,都放入一级缓存同样能解决。(你想,反正最终这些对象都会加载完)
  1. 不提前创立代理对象,在呈现循环依赖时才生成代理对象。
    显然,spring抉择了这种形式。

B.只应用三级缓存,每次通过工厂创立是否可行?

答:不行。

这个比拟容易了解,每次通过工厂创建对象,会毁坏单例。

3.什么是FactoryBean?

除了一般对象之外,spring还为使用者提供了一个FactoryBean接口,用来创立简单对象。(算是一个扩大点)

扩大点

如果一个FactoryBean对象注入到spring中,从SpringContext获取时失去的是一个调用FactoryBean.getObject()创立的一般对象。

有点绕嘴,举例说明一下:

// 申明一个Test对象
class Test{}

// 申明一个TestFactoryBean对象,实现FactoryBean接口
class TestFactoryBean implements FactoryBean<Test> {
    @Override
    public CostForm getObject() throws Exception {
        Test test = new Test();
        return test;
    }

    @Override
    public Class<?> getObjectType() {
        return Test.class;
    }
}

下面的例子中,通过SpringContextUtil.getBean("testFactoryBean")获取的对象不是TestFactoryBean,而是Test

那么如何获取到TestFactoryBean呢,name减少“&”前缀即可。

TestFactoryBean factoryBean = SpringContextUtil.getBean("&testFactoryBean")
Test test = SpringContextUtil.getBean("testFactoryBean");

源码实现

为什么会如此?通过源码来寻找答案。

  • TestFactoryBean对象的注入
// 对象初始化过程有这么一段逻辑
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
preInstantiateSingletons(){
    // 如果是FactoryBean对象,会减少“&”前缀注入
    if (isFactoryBean(beanName)) {
          Object bean = getBean("&" + beanName);
    }
}
  • Test对象的注入

Spring对FactoryBean对象解决的执行入口在这里:

// 后面剖析过的对象初始化逻辑
sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);        
        });
// ## 对象创立后,做非凡解决
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

察看具体做了什么解决:

Object getObjectForBeanInstance(
            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    // -- 非FacotryBean对象,间接返回
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
            return beanInstance;
    }

    // -- 通过FactoryBean获取对象
    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
        ⬇⬇⬇⬇⬇⬇
        // ## 最终会调用这里,执行FatoryBean的getObject办法
        object = factory.getObject();
        ⬆⬆⬆⬆⬆⬆
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}

附录

P6-P7常识合辑

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理