Chinaunix首页 | 论坛 | 博客
  • 博客访问: 23038
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 315
  • 用 户 组: 普通用户
  • 注册时间: 2021-05-20 10:41
文章分类
文章存档

2022年(31)

我的朋友

分类: Java

2022-01-07 11:31:38

首先,需要明确的是spring对循环依赖的处理有三种情况:

①构造器的循环依赖:这种依赖spring是处理不了的,直 接抛出BeanCurrentlylnCreationException异常。

②单例模式下的setter循环依赖:通过“三级缓存”处理循环依赖。

③非单例循环依赖:无法处理。

spring单例对象的初始化大略分为三步:

  1. createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象
  2. populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
  3. initializeBean:调用spring xml中的init 方法。

从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。 接下来,我们具体看看spring是如何处理三种循环依赖的。

1、构造器循环依赖

this .singletonsCurrentlylnCreation.add(beanName)将当前正要创建的bean 记录在缓存中 Spring 容器将每一个正在创建的bean 标识符放在一个“当前创建bean 池”中, bean 标识 柏:在创建过程中将一直保持在这个池中,因此如果在创建bean 过程中发现自己已经在“当前 创建bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;而对于创建 完毕的bean 将从“ 当前创建bean 池”中清除掉。

2、setter循环依赖

Spring为了解决单例的循环依赖问题,使用了三级缓存。

点击(此处)折叠或打开

  1. /** Cache of singleton objects: bean name –> bean instance */
  2. private final Map singletonObjects = new ConcurrentHashMap(256);
  3. /** Cache of singleton factories: bean name –> ObjectFactory */
  4. private final Map> singletonFactories = new HashMap>(16);
  5. /** Cache of early singleton objects: bean name –> bean instance */
  6. private final Map earlySingletonObjects = new HashMap(16)
这三级缓存的作用分别是:


singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级缓存)

earlySingletonObjects :完成实例化但是尚未初始化的,提前暴光的单例对象的Cache (二级缓存)

singletonObjects:完成初始化的单例对象的cache(一级缓存)

我们在创建bean的时候,java培训会首先从cache中获取这个bean,这个缓存就是sigletonObjects。主要的调用方法是:

点击(此处)折叠或打开

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. Object singletonObject = this.singletonObjects.get(beanName);
  3. //isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中
  4. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  5. synchronized (this.singletonObjects) {
  6. singletonObject = this.earlySingletonObjects.get(beanName);
  7. //allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象
  8. if (singletonObject == null && allowEarlyReference) {
  9. ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
  10. if (singletonFactory != null) {
  11. singletonObject = singletonFactory.getObject();
  12. //从singletonFactories中移除,并放入earlySingletonObjects中。
  13. //其实也就是从三级缓存移动到了二级缓存
  14. this.earlySingletonObjects.put(beanName, singletonObject);
  15. this.singletonFactories.remove(beanName);
  16. }
  17. }
  18. }
  19. }
  20. return (singletonObject != NULL_OBJECT ? singletonObject : null);
  21. }

  22. 从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory,定义如下:

  23.         
  24. public interface ObjectFactory {
  25. T getObject() throws BeansException;
  26. }

这个接口在AbstractBeanFactory里实现,并在核心方法doCreateBean()引用下面的方法: 

点击(此处)折叠或打开

  1. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  2.     Assert.notNull(singletonFactory, "Singleton factory must not be null");
  3.     synchronized (this.singletonObjects) {
  4.         if (!this.singletonObjects.containsKey(beanName)) {
  5.             this.singletonFactories.put(beanName, singletonFactory);
  6.             this.earlySingletonObjects.remove(beanName);
  7.             this.registeredSingletons.add(beanName);
  8.         }
  9.     }
  10. }



这段代码发生在createBeanInstance之后,populateBean()之前,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,此时将这个对象提前曝光出来,让大家使用。
这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

3、非单例循环依赖

对于“prototype”作用域bean, Spring 容器无法完成依赖注入,因为Spring 容器不进行缓 存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean 。

相关spring视频学习资料欢迎到尚硅谷官方下载。
阅读(237) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~