什么是循环依赖?就是两个Bean相互引用,比如用@Autowire 相互注入。
 
那么Spring是如何解决这个问题的呢?在Bean还未完全实例化前(类只实例化了一部分),将bean提前暴露出来,可以被其他Bean引用。
 
源码解析:
 
问题1:什么情况下需要提前暴露?
 
Spring托管的bean是通过getBean()-->doCreateBean()创建的。
 
正常情况下,单例模式,第一次调用getBean单例初始化完成后,直接放入cache了,后面再次调用直接从cache拿,不用走doCreateBean 了。
 
0
 
 
当有循环依赖时候,第二次调用getBean代码earlySingletonExposure就会=true,那么
就会触发行提前暴露bean的逻辑。
 
关键代码:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
 
AbstractAutowireCapableBeanFactory.doCreateBean();
 
0
 
 
 
问题2: 提前暴露出去的对象是指定类型的Bean的实例本身吗?
不是的,是一个ObjectFactory用于创建该Bean实例 。因为Spring AOP机制,bean在后续实例化过程中可能会被BeanPostProcess处理,生成一个Proxy对象。
 
问题3: 是怎么提前暴露出去的?
其实很简单,遍历所有的BeanPostProcessor ,看是不是SmartInstantiationAwareBeanPostProcessor 对象(该接口是Spring AOP的顶级接口),不是(不需要AOP)直接返回原始bean。SmartInstantiationAwareBeanPostProcessor 提供了一个后门getEarlyBeanReference,该方法提前先调用了proxy bean的生成方法 wrapIfNecessary(),也就是说,AOP提前切入了。
0
 
 
问题4: 提前暴露出去的bean和最终生成的bean是同一个吗?
SmartInstantiationAwareBeanPostProcessor 接口做了强制规定,要么是同一个proxy对象,要么直接放回原始bean(不需要AOP的类)。
0
 
下面用AbstractAutoProxyCreator这个具体实现类的代码作进一步说明。 方法postProcessAfterInitialization() 加了一个判断,如果之前调用了getEarlyBeanReference(),完成了AOP,这里就不重复调用wrapIfNecessary()了(只是从earlyProxyReferences 做了remove),从而保证是同一个proxy对象。
0
 
 
问题5:很多文档说的循环依赖是通过三级缓存解决的,这个说法是咋回事?
 
上面详细介绍了singletonFactories 怎么暴露出去的,三级缓存就是DefaultSingletonBeanRegistry类里面的三个map,缓存核心逻辑见下段代码,先找singletonObjects,找不到再从earlySingletonObjects找,还是没有直接用singletonFactories 工厂创建(里面对singletonObjects加了锁,防止并发错误)。
0
 
代码实践:
为了证明以上说法,我们简单写一点代码加以验证。
代码:https://gitee.com/zfj321/spring-sourcecode-study
然后在下面位置打一个断点,进行跟踪。
0
 
 
参考资料:
 
 
 
 

Spring循环依赖的问题的更多相关文章

  1. spring循环依赖问题分析

    新搞了一个单点登录的项目,用的cas,要把源码的cas-webapp改造成适合我们业务场景的项目,于是新加了一些spring的配置文件. 但是在项目启动时报错了,错误日志如下: 一月 , :: 下午 ...

  2. Spring 循环依赖

    循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环.此处不 ...

  3. Springboot源码分析之Spring循环依赖揭秘

    摘要: 若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效.或许刚说到这,有的小伙伴就会大惊失色了.Spring不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我 ...

  4. Spring 循环依赖的三种方式(三级缓存解决Set循环依赖问题)

    本篇文章解决以下问题: [1] . Spring循环依赖指的是什么? [2] . Spring能解决哪种情况的循环依赖?不能解决哪种情况? [3] . Spring能解决的循环依赖原理(三级缓存) 一 ...

  5. Spring循环依赖的解决

    ## Spring循环依赖的解决 ### 什么是循环依赖 循环依赖,是依赖关系形成了一个圆环.比如:A对象有一个属性B,那么这时候我们称之为A依赖B,如果这时候B对象里面有一个属性A.那么这时候A和B ...

  6. 这个 Spring 循环依赖的坑,90% 以上的人都不知道

    1. 前言 这两天工作遇到了一个挺有意思的Spring循环依赖的问题,但是这个和以往遇到的循环依赖问题都不太一样,隐藏的相当隐蔽,网络上也很少看到有其他人遇到类似的问题.这里权且称他非典型Spring ...

  7. Spring — 循环依赖

    读完这篇文章你将会收获到 Spring 循环依赖可以分为哪两种 Spring 如何解决 setter 循环依赖 Spring 为何是三级缓存 , 二级不行 ? Spring 为啥不能解决构造器循环依赖 ...

  8. 帮助你更好的理解Spring循环依赖

    网上关于Spring循环依赖的博客太多了,有很多都分析的很深入,写的很用心,甚至还画了时序图.流程图帮助读者理解,我看了后,感觉自己是懂了,但是闭上眼睛,总觉得还没有完全理解,总觉得还有一两个坎过不去 ...

  9. spring 循环依赖的一次 理解

    前言: 在看spring 循环依赖的问题中,知道原理,网上一堆的资料有讲原理. 但今天在看代码过程中,又产生了疑问. 疑问点如下: // 疑问点: 先进行 dependon 判断String[] de ...

  10. 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖

    本次博客的目标 1. 手写spring循环依赖的整个过程 2. spring怎么解决循环依赖 3. 为什么要二级缓存和三级缓存 4. spring有没有解决构造函数的循环依赖 5. spring有没有 ...

随机推荐

  1. OpenSIPS 2.4.2 高并发下,日志丢失怎么办

      问题年年有,今年特别多.最近公司对呼叫中心平台做了大幅度重构,基于OpenSIPS实现的会话管理服务,在高并发压测过程中,发现OpenSIPS的日志居然出现丢失的情况,简直让我食不知味,困惑不已. ...

  2. Laravel Pipeline原理及使用

    Laravel Pipeline原理及使用 开发中可能遇到非常冗长的逻辑,以至于我们想将针对性逻辑拆分出来,但是又拿不准该如何拆分才能实现较高的扩展性并保证较高的维护性,或者说不知道如何优雅的将待处理 ...

  3. Android开发之http网络请求返回码问题集合。

    HTTP状态码(HTTP Status Code) 一些常见的状态码为: 200 - 服务器成功返回网页  404 - 请求的网页不存在  503 - 服务不可用  一.1xx(临时响应) 表示临时响 ...

  4. Photon PUN 三 RPCs & RaiseEvent

    官方文档地址 https://doc.photonengine.com/en-us/pun/current/manuals-and-demos/rpcsandraiseevent 一, RPC   P ...

  5. 使用powershell完成定时get任务

    最近公司网站需要在后台定时请求和更新数据,但PHP毕竟是一种后端语言,不能自动运行,所以整理了几个方法. 1.在前端定时请求页面. 2.使用批处理. 3.使用windows计划任务. 第一种方案必须要 ...

  6. 【HttpRunner v3.x】笔记 ——2. 用脚手架快速创建项目

    环境装好了,相信很多童鞋已经迫不及待的想run起来了,但是面对一个陌生的框架又无从下手.没关系,我们可以用脚手架来快速生成一个httprunner项目. 一.快速生成项目 我们不妨先输入httprun ...

  7. java 注解开发

    目录 注解 JDK自带的注解三个 注解分类 按照运行机制 按照来源分类 自定义注解的语法要求 元注解 解析注解 获取注解的注解 Spring中的注解 组合注解 注解 JDK自带的注解三个 @Overr ...

  8. 深入了解Redis【二】对象及数据结构综述

    引言 Redis中每个键值对都是由对象组成: 键总是一个字符串对象(string) 值可以是字符串对象(string).列表对象(list).哈希对象(hash).集合对象(set).有序集合对象(z ...

  9. linux命令的学习随笔

    getconf PAGE_SIZE //获取内存分页的大小alias vi='vim'//临时生效vi /root/.bashrcwhereis ls输出重定向> >> 2> ...

  10. Java8 日期和时间类

    新的日期和时间API 新的日期和时间类解决了Date和Calendar类出现的问题 浅尝 LocalDate 日期类 LocalDate of = LocalDate.of(2018, 7, 13); ...