前置知识

  • 只有单例模式下的bean会通过三级缓存提前暴露来解决循环依赖的问题。而非单例的bean每次获取都会重新创建,并不会放入三级缓存,所以多实例的bean循环依赖问题不能解决。

  • 首先需要明白处于各个阶段的bean被放在哪里。在DefaultSingletonBeanRegistry类中

  1. /** 一级缓存,存放经历完整生命周期的bean*/
  2. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
  3. /** 三级缓存,存放FactoryBean */
  4. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
  5. /** 二级缓存,存放刚刚创建出来还未进行属性赋值的bean */
  6. private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

测试代码

  1. public class ServiceAA {
  2. public ServiceAA() {
  3. System.out.println("AA已经创建");
  4. }
  5. private ServiceBB serviceBB;
  6. public ServiceBB getServiceBB() {
  7. return serviceBB;
  8. }
  9. public void setServiceBB(ServiceBB serviceBB) {
  10. this.serviceBB = serviceBB;
  11. }
  12. }
  1. public class ServiceBB {
  2. public ServiceBB() {
  3. System.out.println("BB已经创建");
  4. }
  5. private ServiceAA serviceAA;
  6. public ServiceAA getServiceAA() {
  7. return serviceAA;
  8. }
  9. public void setServiceAA(ServiceAA serviceAA) {
  10. this.serviceAA = serviceAA;
  11. }
  12. }
  1. <bean id="serviceAA" class="org.song.circularDepend.ServiceAA" >
  2. <property name="serviceBB" ref="serviceBB"/>
  3. </bean>
  4. <bean id="serviceBB" class="org.song.circularDepend.ServiceBB">
  5. <property name="serviceAA" ref="serviceAA"/>
  6. </bean>

流程

  • 在上述代码中,ServiceAA 依赖于ServiceBB,同时ServiceBB也依赖于ServiceAA ,各个bean相互依赖,最终形成了闭环,那么spring是如何解决此问题的呢?

  • 可以简单理解为spring创建bean分为两个步骤,第一步是创建原始bean,第二步就是对属性进行赋值和初始化等操作。

  • 每次创建bean之前都会去缓存中查找是否有当前bean,因为是单例,只能有一个

  • 当创建bean serviceAA后,会将其加入三级缓存中,然后需要填充bean的属性了

  • 这时候发现需要依赖于bean serviceBB,接下来又去创建serviceBB。重复1-3的流程

  • 这时候发现需要依赖于bean serviceAA,发现三级缓存中存在bean serviceAA,所以不需要重复创建。把bean serviceAA注入bean serviceBB

  • 这个时候bean serviceBB创建好了,递归继续去为bean serviceAA进行属性赋值。闭环完成

源码

  1. public void refresh() throws BeansException, IllegalStateException {
  2. synchronized (this.startupShutdownMonitor) {
  3. try {
  4. //初始化剩下的单实例bean
  5. finishBeanFactoryInitialization(beanFactory);
  6. }
  7. catch (BeansException ex) {
  8. if (logger.isWarnEnabled()) {
  9. logger.warn("Exception encountered during context initialization - " +
  10. "cancelling refresh attempt: " + ex);
  11. }
  12. // Destroy already created singletons to avoid dangling resources.
  13. destroyBeans();
  14. // Reset 'active' flag.
  15. cancelRefresh(ex);
  16. // Propagate exception to caller.
  17. throw ex;
  18. }
  19. finally {
  20. // Reset common introspection caches in Spring's core, since we
  21. // might not ever need metadata for singleton beans anymore...
  22. resetCommonCaches();
  23. }
  24. }
  25. }
  1. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  2. //实例化单实例bean
  3. beanFactory.preInstantiateSingletons();
  4. }
  1. @Override
  2. public void preInstantiateSingletons() throws BeansException {
  3. getBean(beanName);
  4. }
  1. public Object getBean(String name) throws BeansException {
  2. return doGetBean(name, null, null, false);
  3. }
  1. protected <T> T doGetBean(
  2. final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
  3. throws BeansException {
  4. final String beanName = transformedBeanName(name);
  5. Object bean;
  6. //首先查询缓存中是否存在bean,当前为null
  7. Object sharedInstance = getSingleton(beanName);
  8. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  9. if (mbd.isSingleton()) {
  10. sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
  11. @Override
  12. public Object getObject() throws BeansException {
  13. try {
  14. //创建bean
  15. return createBean(beanName, mbd, args);
  16. }
  17. catch (BeansException ex) {
  18. destroySingleton(beanName);
  19. throw ex;
  20. }
  21. }
  22. });
  23. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  24. }
  25. }
  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. //一级缓存中是否存在当前bean(serviceAA),此时为null
  3. //isSingletonCurrentlyInCreation(beanName),当前bean是否被标记为
  4. //正在创建,返回false,所以后续不执行,直接返回null
  5. Object singletonObject = this.singletonObjects.get(beanName);
  6. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  7. synchronized (this.singletonObjects) {
  8. singletonObject = this.earlySingletonObjects.get(beanName);
  9. if (singletonObject == null && allowEarlyReference) {
  10. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  11. if (singletonFactory != null) {
  12. singletonObject = singletonFactory.getObject();
  13. this.earlySingletonObjects.put(beanName, singletonObject);
  14. this.singletonFactories.remove(beanName);
  15. }
  16. }
  17. }
  18. }
  19. return (singletonObject != NULL_OBJECT ? singletonObject : null);
  20. }
  1. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  2. beforeSingletonCreation(beanName);
  3. singletonObject = singletonFactory.getObject();
  4. addSingleton(beanName, singletonObject);
  5. return (singletonObject != NULL_OBJECT ? singletonObject : null);
  6. }
  1. protected void beforeSingletonCreation(String beanName) {
  2. //将当前正在创建的bean serviceAA 标记为正在创建中,添加到集合singletonsCurrentlyInCreation中
  3. if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
  4. throw new BeanCurrentlyInCreationException(beanName);
  5. }
  6. }
  1. protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
  2. //开始真正的创建bean service AA
  3. Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  4. return beanInstance;
  5. }
  1. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
  2. throws BeanCreationException {
  3. BeanWrapper instanceWrapper = null;
  4. //用反射创建初始bean serviceAA,源码部分略过
  5. instanceWrapper = createBeanInstance(beanName, mbd, args);
  6. //当前为true
  7. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
  8. if (earlySingletonExposure) {
  9. //如果一级缓存中没有当前bean,就将当前bean放入三级缓存
  10. addSingletonFactory(beanName, new ObjectFactory<Object>() {
  11. @Override
  12. public Object getObject() throws BeansException {
  13. return getEarlyBeanReference(beanName, mbd, bean);
  14. }
  15. });
  16. }
  17. // 初始化bean
  18. Object exposedObject = bean;
  19. //为当前bean serviceAA赋值
  20. populateBean(beanName, mbd, instanceWrapper);
  21. if (earlySingletonExposure) {
  22. Object earlySingletonReference = getSingleton(beanName, false);
  23. }
  24. return exposedObject;
  25. }
  1. protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  2. Assert.notNull(singletonFactory, "Singleton factory must not be null");
  3. //将当前bean serviceAA放入三级缓存,key为beanname value为创建该bean的工厂
  4. synchronized (this.singletonObjects) {
  5. if (!this.singletonObjects.containsKey(beanName)) {
  6. this.singletonFactories.put(beanName, singletonFactory);
  7. this.earlySingletonObjects.remove(beanName);
  8. this.registeredSingletons.add(beanName);
  9. }
  10. }
  11. }
  1. protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
  2. //获取当前bean sercieAA所有的属性
  3. PropertyValues pvs = mbd.getPropertyValues();
  4. //......对属性的解析等操作略过
  5. //开始注入属性
  6. applyPropertyValues(beanName, mbd, bw, pvs);
  7. }
  1. protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
  2. //封装所有属性的集合,略过
  3. List<PropertyValue> original;
  4. for (PropertyValue pv : original) {
  5. String propertyName = pv.getName();
  6. Object originalValue = pv.getValue();
  7. //解析引用类型的属性
  8. Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
  9. }
  10. //应用属性过程略过
  11. }
  1. public Object resolveValueIfNecessary(Object argName, Object value) {
  2. //判断是否是运行时bean引用
  3. if (value instanceof RuntimeBeanReference) {
  4. RuntimeBeanReference ref = (RuntimeBeanReference) value;
  5. //解析此引用 service BB
  6. return resolveReference(argName, ref);
  7. }
  1. private Object resolveReference(Object argName, RuntimeBeanReference ref) {
  2. String refName = ref.getBeanName();
  3. refName = String.valueOf(doEvaluate(refName));
  4. //开始递归调用getbean方法,当前bean为serviceBB
  5. Object bean = this.beanFactory.getBean(refName);
  6. }
  • 此时serviceBB开始递归调用getBean()方法,开始重复上述流程,关注流程第五步。此时bean serviceBB实例化完成,并且开始为属性赋值,发现有一个属性为serviceAA。于是尝试获取serviceAA。

  • 进入this.beanFactory.getBean()方法。进入doGetBean()方法。后进入getSingleton(String beanName, boolean allowEarlyReference)

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. //一级缓存中是否存在当前bean(serviceAA),此时为null
  3. //isSingletonCurrentlyInCreation(beanName),当前bean是否被标记为
  4. //正在创建,当前为true,进入判断
  5. Object singletonObject = this.singletonObjects.get(beanName);
  6. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  7. synchronized (this.singletonObjects) {
  8. //判断二级缓存中是否有bean serviceAA,当前返回null
  9. singletonObject = this.earlySingletonObjects.get(beanName);
  10. if (singletonObject == null && allowEarlyReference) {
  11. //通过beanname获得了之前在三级缓存中添加的创建serviceAA的工厂方法
  12. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  13. if (singletonFactory != null) {
  14. //此时singletonObject指向之前实例化完成的serviceAA对象
  15. singletonObject = singletonFactory.getObject();
  16. //将serviceAA添加到二级缓存中,并从三级缓存中删除
  17. this.earlySingletonObjects.put(beanName, singletonObject);
  18. this.singletonFactories.remove(beanName);
  19. }
  20. }
  21. }
  22. }
  23. return (singletonObject != NULL_OBJECT ? singletonObject : null);
  24. }
  • 此时serviceBB的属性赋值完毕,serviceBB的populateBean方法执行结束。其属性service AA指向还未执行完成populateBean()方法的bean serviceAA,等待后续的递归返回。
  1. protected void addSingleton(String beanName, Object singletonObject) {
  2. synchronized (this.singletonObjects) {
  3. //将serviceBB放入一级缓存
  4. this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
  5. //从三级缓存,二级缓存中删除
  6. this.singletonFactories.remove(beanName);
  7. this.earlySingletonObjects.remove(beanName);
  8. this.registeredSingletons.add(beanName);
  9. }
  10. }
  • 此时serviceBB创建完毕。递归返回继续还未属性赋值完成的bean serviceAA。调用 addSingleton()方法,同上将bean serviceAA放入一级缓存

总结

欢迎关注公众号:前程有光,领取一线大厂Java面试题总结+各知识点学习思维导+一份300页pdf文档的Java核心知识点总结!

建议收藏!利用Spring解决循环依赖,深入源码给你讲明白!的更多相关文章

  1. Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗

    开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...

  2. Spring解决循环依赖

    1.Spring解决循环依赖 什么是循环依赖:比如A引用B,B引用C,C引用A,它们最终形成一个依赖环. 循环依赖有两种 1.构造器循环依赖 构造器注入导致的循环依赖,Spring是无法解决的,只能抛 ...

  3. springboot bean的循环依赖实现 源码分析

    springboot bean的循环依赖实现 源码分析 本文基于springboot版本2.5.1 <parent> <groupId>org.springframework. ...

  4. 曹工说Spring Boot源码(29)-- Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  6. Spring解决循环依赖,你真的懂了吗?

    导读 前几天发表的文章SpringBoot多数据源动态切换和SpringBoot整合多数据源的巨坑中,提到了一个坑就是动态数据源添加@Primary接口就会造成循环依赖异常,如下图: 这个就是典型的构 ...

  7. 一张图彻底理解Spring如何解决循环依赖!!

    写在前面 最近,在看Spring源码,看到Spring解决循环依赖问题的源码时,不得不说,源码写的太烂了.像Spring这种顶级的项目源码,竟然存在着这种xxx的代码.看了几次都有点头大,相信很多小伙 ...

  8. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  9. 彻底理解Spring如何解决循环依赖

    Spring bean生命周期 可以简化为以下5步. 1.构建BeanDefinition 2.实例化 Instantiation 3.属性赋值 Populate 4.初始化 Initializati ...

随机推荐

  1. fastjson JSONObject简单使用

    工作中用的蛮多的最近整理下,概括地说 通过这个工具可以让一个json在json串,JSONObject,java对象之间进行转化 首先我们先写2个bean来构成一个比较复杂的json串: public ...

  2. 网络拓扑实例09:VRRP组网下同网段内配置基于全局地址池的DHCP服务器

    组网图形 DHCP服务器简介 见前面DHCP服务器文章,不再赘述. 组网需求 如图1所示,某企业内的一台主机通过Switch双归属到SwitchA和SwitchB,SwitchA为主设备,作为DHCP ...

  3. Python学习随笔:使用xlwings设置和操作excel多行多列数据以及设置数据字体颜色填充色对齐方式的方法

    ☞ ░ 前往老猿Python博文目录 ░ 在前面老猿的文章中,<Python学习随笔:使用xlwings读取和操作Excel文件>.<Python学习随笔:使用xlwings读取和操 ...

  4. 什么是Python生成器?与迭代器的关系是什么?

    生成器是一个特殊的迭代器,它保存的是算法,每次调用next()或send()就计算出下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration.生成器有两种类型,一种是生 ...

  5. PyQt(Python+Qt)学习随笔:QListWidget的信号简介

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 不含继承信号,QListWidget自身的信号包括如下: currentItemChanged(QL ...

  6. 手把手教你爬取B站弹幕!

    效果 输入要爬取的视频的BV号即可爬取该视频的弹幕. 过程 基本思路 基本的思路很简单,还是老步骤: 1.构造爬取的url 2.解析返回的数据 3.使用json或Xpath或正则表达式提取数据 4.保 ...

  7. 动态svg图片简单制作

    一.简介 #topics #no-box-shadow-img { box-shadow: none } 博主头像 svg图片格式不同于其它图片格式,svg图片本质上是一个xml文件,它内部是标记语言 ...

  8. Object.prototype.toString.call()和typeof()区别

    js中判断数据类型都知道用tyoeof(),但是他只能判断object,boolean,function,string,number,undefined还有symbol(es6新增)这几种初步类型,比 ...

  9. SNOI2020 部分题解

    D1T1 画图可以发现,多了一条边过后的图是串并联图.(暂时不确定) 然后我们考虑把问题变成,若生成树包含一条边\(e\),则使生成树权值乘上\(a_e\),否则乘上\(b_e\),求最终的生成树权值 ...

  10. 题解-CF1437F Emotional Fishermen

    题面 CF1437F Emotional Fishermen 给 \(n\) 个数的序列 \(a_i\),求有多少种 \(n\) 个数的排列 \(p_i\),使得 \[\frac{a_{p_i}}{\ ...