从源码层面解读spring中bean的循环依赖解决方案(2)

写在前面的话

我们最终都要远行,

最终都要跟稚嫩的自己告别。

也许路途注定艰辛,注定孤独,

但熬过了痛苦,我们才能得以成长,

要知道生活把我们磨圆是为了让我们滚得更远。

之前我们已经结束了依赖循环的定义种类和解决原理,接下来我们从源码层面来解读下spring是如何实现的解决依赖循环。

让我们先了解一些前置知识点,

FactoryBean

FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。

一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。

但是在某些特殊情况下,实例化bean过程比较复杂,属性要求很多,这时如果再继续采用传统的xml配置方式,

就需要在</bean/>中提供大量的配置信息了,灵活性受到了限制。

在这样的背景下,就产生了采用编码方式提供一个org.springframework.beans.factory.FactoryBean的工厂类,用户可以通过实现该接口定制化实例化bean逻辑的简单方案。

以下是源码:

public interface FactoryBean<T> {
  @Nullable
  T getObject() throws Exception;
  @Nullable
  Class<?> getObjectType();
  default boolean isSingleton() {
     return true;
  }
}

定义了三个方法:

  • T getObject():返回FactoryBaen创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。

  • Class<?> getObjectType():返回FactoryBean创建的bean类型。

  • boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。

具体解释下:

当配置文件中</bean/>的class属性配置的实现类是FactoryBean时,

通过getBean()方法放回的不是FactoryBean本身,

而是FactoryBean#getObject()方法返回的对象,相当于FactoryBean#getObject()代理了getBean()。

从缓存中获取单例bean

接下来我们就可以了解bean的加载过程了。

下面就是我们重要的原理了:

单例在Spring的同一个容器内只会被创建一次,

后续需要再获取bean时直接从单例缓存中获取即可,

当然这里也只是尝试创建,首先尝试从缓存中加载,

然后再次尝试从singletonFactories中加载。

这样加载的原因是:

因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,

Spring创建bean的原则是不等bean创建完成就会将创建的ObjectFactory提早曝光加入到缓存中,

一旦下一个bean创建需要依赖到上一个bean时,就可以直接使用ObjectFactory。

public Object getSingleton(String beanName) {
   //参数true设置标识运行早期依赖
   return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //检查缓存中是否存在实例
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      //如果为空,则锁定全局变量并进行处理
      synchronized (this.singletonObjects) {
         //如果当前bean正在加载则不处理
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            //当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的
            //ObjectFactory初始化策略存储在singletonFactories中
            //这里就是将存储的ObjectFactory取出来操作。
            ObjectFactory/</?/>/ singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               //调用预先设定的getObject方法
               singletonObject = singletonFactory.getObject();
               //记录在缓存中,earlySingletonObjects和singletonFactories互斥
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

接下来解读下上面的源代码

在这个方法中我们就已经涉及到了关键的循环依赖检测了同时也涉及很多变量的记录存取。

方法分下面步骤:

  • 1.首先尝试从singletonObject里面获取实例,

  • 2.如果获取不到就会从earlySingletonObjects里面获取,

  • 3.如果还是获取不到,才会从singletonFactories里面获取beanName对应的ObjectFactory,

调用这个ObjectFactory的getObjiect来创建bean,

  • 4.将bean放到earlySingletonObjects里面去,同时从singletonFactories里面remove

掉上面获取到的ObjectFactory,

而对于后续所有内存操作都只是为了循环依赖检测时使用,也就是源码中的allowEarlyReference为true的情况下才会使用。

上面涉及到多个存储bean的map,就是我们常说的3级缓存。

到这我们可以说:Spring就是通过三级缓存来解决循环依赖的。

/**1级缓存,单例对象的缓存:bean名称——> bean实例*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);


/** 2级缓存,早期单例对象的缓存:bean名称——> bean实例 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** 3级缓存,单例工厂的缓存:bean名称——> ObjectFactory*/
private final Map<String, ObjectFactory<?/>/>/ singletonFactories = new HashMap<>(16);

  • singletonObjects:用于保存BeanName和创建bean实例之前的关系,bean name–>bean instance。
  • earlySingletonObjects:也是保存BeanName和创建bean实例之前的关系,
    但是与singletonObjects的不同之处在于,当一个单例bean被加入这里之后,

即使bean还在创建过程中,也可以通过getBean方法获取到了,其目的是用来检测循环引用。

  • singletonFactories:用于保存BeanName和创建bean的工厂之间的关系,
    bean name–>ObjectFactory。

写在后面的话

到这里我们已经知道了解决方案是Spring的3级缓存,通过提前暴露创建bean的ObjectFactory工厂,将其加入缓存,

获取单例的时候优先在去缓存中找,如果没有找到再进行bean的创建。

某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories中,

至于如何将bean初始化,在什么时候初始化,如果缓存中没有bean时会怎么办?

我们下一篇会继续解释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值