Spring循环依赖

Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)_java 常见面试题详解(locksupport-CSDN博客

循环依赖现象在spring容器中注入依赖的对象,有2种情况

  • 构造器方式注入依赖(不可行)
  • 以set方式注入依赖(可行)

重要结论(spring内部通过3级缓存来解决循环依赖) - DefaultSingletonBeanRegistry

只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以多例的bean是没有缓存的,不会将其放到三级缓存中。原型(Prototype)的场景是不支持循环依赖的,会报错

  • 第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象。
  • 第二级缓存:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完。
  • 第三级缓存:Map<String, ObjectFactory<?>> singletonFactories,存放可以生成Bean的工厂。

package org.springframework.beans.factory.support;

...

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	...

	/** 
	单例对象的缓存:bean名称—bean实例,即:所谓的单例池。
	表示已经经历了完整生命周期的Bean对象
	第一级缓存
	*/
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/**
	早期的单例对象的高速缓存: bean名称—bean实例。
	表示 Bean的生命周期还没走完(Bean的属性还未填充)就把这个 Bean存入该缓存中也就是实例化但未初始化的 bean放入该缓存里
	第二级缓存
	*/
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

	/**
	单例工厂的高速缓存:bean名称—ObjectFactory
	表示存放生成 bean的工厂
	第三级缓存
	*/
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
 
    ...
    
}

 

  1. A创建过程中需要B,于是A将自己放到三级缓里面,去实例化B。
  2. B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。
  3. B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

Spring创建 bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化

每次创建 bean之前,我们都会从缓存中查下有没有该bean,因为是单例,只能有一个

当我们创建 beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了beanB,接着就又去创建beanB,同样的流程,创建完beanB填充属性时又发现它依赖了beanA又是同样的流程,
不同的是:这时候可以在三级缓存中查到刚放进去的原始对象beanA.所以不需要继续创建,用它注入 beanB,完成 beanB的创建

既然 beanB创建好了,所以 beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成

 

Spring解决循环依赖依靠的是Bean的"中间态"这个概念,而这个中间态指的是已经实例化但还没初始化的状态—>半成债。实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。”对

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

其中一级缓存为单例池(singletonObjects)。

二级缓存为提前曝光对象(earlySingletonObjects)。

三级级存为提前曝光对象工厂(singletonFactories) 。

假设A、B循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。

相关推荐

  1. Spring 循环依赖详解

    2024-04-09 23:18:05       8 阅读
  2. Spring如何解决循环依赖

    2024-04-09 23:18:05       21 阅读
  3. Spring如何解决循环依赖

    2024-04-09 23:18:05       17 阅读
  4. spring循环依赖解决方案

    2024-04-09 23:18:05       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-09 23:18:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-09 23:18:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-09 23:18:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-09 23:18:05       18 阅读

热门阅读

  1. 抓包之linux下tcpdump命令

    2024-04-09 23:18:05       8 阅读
  2. React中的函数组件和类组件的区别

    2024-04-09 23:18:05       10 阅读
  3. lodash 使用

    2024-04-09 23:18:05       13 阅读
  4. OceanBase V4.X中常用的SQL(一)

    2024-04-09 23:18:05       12 阅读
  5. CentOS运行级别

    2024-04-09 23:18:05       15 阅读
  6. 用GCC把C语言文件编译成Intel语法的汇编代码

    2024-04-09 23:18:05       12 阅读
  7. PHP基础

    PHP基础

    2024-04-09 23:18:05      12 阅读
  8. Linux 配置dotnet 程序服务

    2024-04-09 23:18:05       13 阅读
  9. 【iOS ARKit】USDZ文件

    2024-04-09 23:18:05       16 阅读