spring源码学习 getSingleton()方法

概述

getSingleton()是位于DefaultSingletonBeanRegistry类中的方法,是用来获取bean的单例实例的。在创建bean实例的时候都会用到这个方法,而获取的单例实例,也会有多个来源。

getSingleton()共有三个级别的缓存,一级缓存中存放实例化完成的bean,二级缓存存放循环依赖的bean实例(占位符),三级缓存中是bean的工厂。

getSingleton()方法首先检查一级缓存,如果没有,检查二级缓存,再没有,进入三级缓存,调用工厂对象来创建对象。一级和二级缓存是直接获取bean实例引用的。

getSingleton()用于获取单例bean,其最大的难点在于循环依赖的解决。spring通过缓存机制+提前加载来解决循环依赖问题。

在DefaultSingletonBeanRegistry中,有三个getSingleton()方法。底层是两个getSingleton()方法,分别是getSingleton(String beanName, boolean allowEarlyReference)和getSingleton(String beanName, ObjectFactory<?> singletonFactory)。后者是spring3.0后新添加的方法。

源码解析

getSingleton有两种版本的实现,在传入的形参上有所不同,区别在于是否会传入singletonFactory类对象。

什么是Singleton?

Singleton是spring的一个作用域,当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。

需要注意的是,spring创建bean实例默认的作用域就是Singleton,单例模式。

除此之外。spring的作用域还有Prototype(多例模式),request、session(这两者只会用于web应用)。

在实例化bean的时候,最大的问题就是解决循环依赖问题了。

那么在spring的单例模式中,是如何解决循环依赖的问题呢?

Spring中通过set方式注入形成的循环依赖是先将提前暴露刚实例化但是未进行其他任何处理的Bean的实例引用,这样返回了Bean实例的引用,后期对bean的任何处理,不会改变bean的引用地址。实际实现上提前暴露一个单例工厂方法,返回了一个在创建中的Bean,这样其他Bean就能够获得他的引用。

spring的getSingleton()方法源码如下:

/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * 返回在给定名称下注册的(原始)singleton对象。
 * 检查已经实例化的singleton,并允许早期对当前创建的单例的引用(解析循环引用)
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // Quick check for existing instance without full singleton lock
   // 快速检查没有完整单例锁的现有实例,如果有就直接返回,如果没有,进入创建流程
   // 先从一级缓存中获取
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      // earlySingletonObjects:早期singleton对象的缓存:bean名称到bean实例。
      // 从二级缓存中获取早期的bean实例
      singletonObject = this.earlySingletonObjects.get(beanName);
      if (singletonObject == null && allowEarlyReference) {
         synchronized (this.singletonObjects) {
            // Consistent creation of early reference within full singleton lock
            // 在完整的单例锁中一致地创建早期引用
            // this.singletonObjects:singleton对象的缓存:bean名称到bean实例
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               // 这里为什么要再次从earlySingletonObjects中获取一次?
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null) {
                  // 从各级缓存中都没有获取到singletonObject,进入创建流程
                  // 从三级缓存中获取工厂对象
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                     singletonObject = singletonFactory.getObject();
                     // 存入二级缓存
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     // 从三级缓存中删除
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}

缓存机制

singletonObjects:一级缓存,里面保存的是一个已经完全实例化完整的bean

earlySingletonObjects:二级缓存,里面保存的是一个已经初始化,尚未实例化的对象

singletonFactories:三级缓存,里面保存了一个对象生成的ObjectFactory方法,里面会调用createBean方法

还有另外两个属性:

  1. singletonsCurrentlyInCreation:正在创建的单例名称队列

  2. registeredSingletons:已经创建成功的单例名称列表

earlySingletonObjects二级缓存和循环依赖问题是息息相关的,对象在创建之后,进行注入过程中,发现产生了循环依赖,那么会将对象放入到这个队列,并且从singletonFactories中移除掉。

 

spring3.0版本后新增的getSingleton()方法源码:

这个类是spring3.0后新增的方法。它允许用户在第一次调用时指定一个 ObjectFactory 对象,该对象负责创建 bean 实例。如果在后续调用中再次调用该方法,则会再次调用 ObjectFactory 对象来创建 bean 实例。

其基础的逻辑,和老版本方法是一致的。

/**
 * Return the (raw) singleton object registered under the given name,
 * creating and registering a new one if none registered yet.
 * @param beanName the name of the bean
 * 返回在给定名称下注册的(原始)singleton对象,如果还没有注册,则创建并注册一个新的。
 * @param singletonFactory the ObjectFactory to lazily create the singleton
 * with, if necessary
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   // 上线程锁
   synchronized (this.singletonObjects) {
      // 从一级缓存中获取
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         // 如果当前beanFactory正在被销毁则直接抛出异常,不允许创建单例bean
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         if (logger.isDebugEnabled()) {
            // 这里日志说的是,创建singleton bean的共享实例
            logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
         }
         // 前置处理
         // 将bean加入singletonsCurrentlyInCreation,用于循环依赖的校验
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         // 核心的创建逻辑还是在这里,和老版本方法的差距在于,
         // 老版本方法从三级缓存中获取单例工厂。新版本使用形参中用户提供的单例工厂
         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // 这里主要是异常处理逻辑,不过多赘述
            // Has the singleton object implicitly appeared in the meantime ->
            // if yes, proceed with it since the exception indicates that state.
            // singleton对象是否同时隐式出现,如果出现了,继续执行,因为异常指示该状态
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               throw ex;
            }
         }
         catch (BeanCreationException ex) {
            if (recordSuppressedExceptions) {
               for (Exception suppressedException : this.suppressedExceptions) {
                  ex.addRelatedCause(suppressedException);
               }
            }
            throw ex;
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            // 后置处理
            // 主要将bean从singletonsCurrentlyInCreation中移除
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            // 加入到缓存中,并调整各级缓存中的状态记录
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

getSingleton()方法主要是做了以下几件事:

  1. 调用ObjectFactory.getObject之前将beanName加入到singletonsCurrentlyInCreation

    1. beforeSingletonCreation(beanName);通过这个方法,在创建单例前进行一些准备工作。这个方法的源码也十分简短。

    2. 判断下当前bean是否需要创建时的校验,并将其加入正在实例化的bean的列表

  2. 调用ObjectFactory.getObject实例化单例

    1. 这里会调用工厂类中的createBean()方法

  3. 设置相关属性 singletonObjects,singletonFactories,earlySingletonObjects,registeredSingletons,这四个属性的作用上文已经提过了。

    1. createBean()方法被调用之后,会根据各种情况,调整缓存中的内容,这个过程是比较复杂的。

    2. 在高版本中的spring,后置处理封装在afterSingletonCreation()和addSingleton()中。在缓存单例集中管理各级缓存中的内容。

 beforeSingletonCreation(beanName):

/**
 * Callback before singleton creation.
 * <p>The default implementation register the singleton as currently in creation.
 * @param beanName the name of the singleton about to be created
 * @see #isSingletonCurrentlyInCreation
 */
protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

 afterSingletonCreation(beanName):

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():

/**
 * Add the given singleton object to the singleton cache of this factory.
 * 将给定的singleton对象添加到此工厂的singleton缓存中。
 * <p>To be called for eager registration of singletons.
 * @param beanName the name of the bean
 * @param singletonObject the singleton object
 */
protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}

那么,缓存机制是如何运行的?

现在设有两个bean,分别叫做A和B,A中有成员变量B,B中有成员变量A,他们构成了一个标准的循环依赖。

spring首先加载到A,实例化A,前期一切如常,执行完工厂的getObject()方法之后,也就是初始化了A,但尚未对A进行属性注入的时候,会开始调整各级缓存中的内容。此时的各级缓存的情况如下:

A:一级缓存中没有,二级缓存中没有,三级缓存中有

这一次getSingleton()方法执行完成后,缓存的状态就是上面这样的。

在后面的populateBean()方法执行的时候,会在A的内部注入B,此时,需要去获取B的实例,还是正常的通过getBean()方法去初始化B,到这一步,缓存的情况是这样子的:

A:一级缓存中没有,二级缓存中没有,三级缓存中有

B:一级缓存中没有,二级缓存中没有,三级缓存中有

随后会执行populateBean()方法去填充B中的属性,但这个时候会有问题了,B中有一个属性是A,而A,尚未实例化完毕。此时此刻,再去调用getSingleton()方法获取A的实例,就会有不一样的结果。

一级缓存中没有A,说明A没有创建完成,但此时的正在创建的bean的列表中,是有A的,说明A正在创建,尚未完成。

再去二级缓存中获取A,由于B是第一个在创建过程中和A之间存在循环依赖的,所以此时二级缓存中也没有A。

再去三级缓存中找A,由于A已经进入了实例化流程,且完成了初始化,因此,三级缓存中是有A的。取出三级缓存中的A,其实是AFactory,执行getObject()拿到A的实例的引用(其实本质上A的引用是个占位符,A在内存中的地址,在他被初始化的那一刻,就已经确定了,不会变了)。将其装入二级缓存,并从三级缓存中清除A。

此时的缓存情况

A:一级缓存中没有,二级缓存中有,三级缓存中没有

B:一级缓存中没有,二级缓存没有,三级缓存中有

然后B继续执行下去,createBean()方法执行完毕,后续方法也正常执行,B成功创建,但B中的A的属性尚未注入。此时的缓存情况如下

A:一级缓存中没有,二级缓存中有,三级缓存中没有

B:一级缓存中有,二级缓存中没有,三级缓存中没有,正在创建的bean列表中没有

此时循环依赖的问题就已经解决了,B已被成功创建。回到A的创建逻辑,拿到B的实例,A将其注入到自己的属性中,A也完成创建。此时的缓存情况如下:

A:一级缓存中有,二级缓存中有,三级缓存中没有,正在创建的bean列表中没有

B:一级缓存中有,二级缓存中没有,三级缓存中没有,正在创建的bean列表中没有

DefaultSingletonBeanRegistry类

这个方法是位于DefaultSingletonBeanRegistry类中,DefaultSingletonBeanRegistry类的作用如下所示:

DefaultSingletonBeanRegistry介绍

DefaultSingletonBeanRegistry 是 BeanFactory 的抽象实现,它提供了 BeanFactory 的大部分功能,包括单例 bean 的注册和获取。

DefaultSingletonBeanRegistry 实现了 SingletonBeanRegistry 接口,该接口定义了单例 bean 的注册和获取方法。

DefaultSingletonBeanRegistry 还实现了 BeanFactory 接口,该接口定义了 bean 的注册、获取、查找和销毁等方法。

DefaultSingletonBeanRegistry 通过使用一个 Map 来存储单例 bean 的引用,当 bean 被注册时,会将 bean 的引用存储到 Map 中。当 bean 被获取时,会从 Map 中获取 bean 的引用。

DefaultSingletonBeanRegistry 还提供了一些方法来管理单例 bean,例如:

  1. 注册单例 bean:可以通过 BeanDefinition 对象来注册单例 bean。

  2. 获取单例 bean:可以通过 bean 的名字来获取单例 bean。

  3. 销毁单例 bean:可以通过 bean 的名字来销毁单例 bean。

总结

说回getSingleton(),这个方法的功能其实也并不复杂。他的执行流程如下:

  1. 首先尝试从已经完成实例化的单例缓存(一级缓存)和提前暴露bean缓存(二级缓存)中获取bean,如果没有,则创建bean:

  2. 标注该bean正在创建中

  3. 使用singletonFactory.getObject()创建对象

  4. 取消bean创建中的标记

  5. 创建完成,添加到相应缓存中,并且从一些缓存中移除出去。

getSingleton()方法中最核心的点就是在于创建/获取单例的时候,解决循环依赖的问题。这里利用三级缓存中的第二级缓存来解决循环依赖的问题。

参考文章

Spring中如何解决循环依赖,singletonObjects,singletonFactories,earlySingletonObjects,一级缓存,二级缓存,三级缓存_spring二级缓存何时清除的-CSDN博客

Spring学习笔记(五)。Bean的作用域,Singleton【单例模式】,Prototype【原型模式】,Request,Session_spring bean作用域单例模式-CSDN博客

Spring源码深度解析:八、bean的获取② - getSingleton_getsingleton 在哪个类下-CSDN博客 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值