面试问Spring循环依赖?今天通过代码调试让你记住

简介: 该文章讨论了Spring框架中循环依赖的概念,并通过代码示例帮助读者理解这一概念。

说明

看这篇文章的同学需要有对Spring ioc和di流程有了解,知道Spring bean创建和bean属性填充。

回忆从Spring容器获取bean

首先,我们可以找到Spring获取bean的方法,它会从三个缓存中获取。

    //一级缓存,存储可以直接使用的bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //二级缓存 存储不完整对象 不能直接用的 循环依赖时,某些依赖的属性没有设置,这个时候bean就是不完整对象
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    //三级缓冲 存储bean工厂 可以延迟调用实例化 使用的时候才调用工厂方法获取对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //从Spring容器获取bean对象方法
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   
   
    //一级缓存
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
   
   
            synchronized (this.singletonObjects) {
   
   
            //二级缓存
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
   
   
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
   
   
                    //三级缓存
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

这里为什么需要3个缓存呢?为啥2个不行?1个也不行?我们下面开始来看到底为什么?

模拟循环依赖场景

A,B类分别有对方的类属性

@Service
public class A {
   
   

    private B b;
}
@Service
public class B {
   
   

    private A a;
}

模拟循环依赖相互手动注入

a对象依赖b对象,b对象又依赖a对象,属性注入流程就是先实例化B,注入A,然后再填充A

A a = new A();

B b = new B();

b.setA(a); //B里面注入不完整的A了

a.setB(b); //注入依赖的对象,A里面有B了,a注入完成变成完整的a

调试Spring循环依赖注入处理流程

相同的场景,看看Spring如何处理。

第一步,创建A类的对象a

image.png 在Spring中,A类实例化后,缓存了一个工厂,而不是实例A,why?

第二步 开始注入a对象的属性b

image.png a注入b的的时候,缓存注册工厂类,而不是b实例

第三步 b对象又开始注入属性a对象

image.png b对象又开始注入a对象了,这个时候a只有对应的工厂。

通过A工厂返回了a的代理对象,将代理对象放入到提早暴露的缓存earlySingletonObjects(二级缓存),这个时候的a并没有将b注入完成,还是一个半成品,所以a还是先放入到一个中间缓存。 image.png

第四步 b对象注入a对象

image.png 这个时候B注入完成了,A还没注入完成。B里面的A里面的B没有赋值

第五步 注入完整B对象,(实际还不是完整的)

image.png

第6步 a对象的b属性注入

image.png 最后将A对象的B属性注入,这个时候A对象完整了,所以B对象也完整了(弥补上一步骤)

完整a对象图

image.png

完整b对象图

image.png

总结一下,为什么使用三级缓存?

下次面试再被问到循环依赖的问题,我会怎么来给面试官说明白呢?

1、Spring通过3个缓存map来解决循环依赖的问题,分别是singletonObjects,singletonFactories,earlySingletonObjects

2、singletonObjects这个缓存是存储完整的对象,可以直接使用的。

3、singletonFactories这个缓存是为了延迟初始化,是解决循环依赖的关键,存在循环依赖注入时,可能注入的对象需要被代理,这个工厂类来实例化一个代理类。

4、earlySingletonObjects这个缓存是为了在a注入b,而b又注入a这个阶段,a是一个半成品,需要用来存储a这种半成品bean的状态。

需要等待a将b注入之后,这个缓存就要移除。

5、singletonFactories和earlySingletonObjects都是存储对象的中间状态,依赖它们保证最终注入的对象是完整的,依赖注入完成后会被清除。

相关文章
|
28天前
|
前端开发 安全 Java
2025春招,Spring 面试题汇总
本文详细整理了2025年春招必备的Spring面试题,分为基础和高级两大部分,帮助求职者全面掌握Spring相关知识点,结合实际项目经验,提升面试成功率。内容涉及Spring框架、AOP、事务管理、数据库集成、Spring Boot、Spring Security、微服务架构等,助力你在春招中脱颖而出。
196 0
|
2月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
77 2
|
2月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
3月前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
3月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
138 2
|
3月前
|
缓存 监控 Java
|
4月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
922 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
4月前
|
设计模式 缓存 Java
面试题:谈谈Spring用到了哪些设计模式?
面试题:谈谈Spring用到了哪些设计模式?
|
4月前
ext portal+dwr+spring实现个性主页面拖拉效果的核心代码
ext portal+dwr+spring实现个性主页面拖拉效果的核心代码
60 7
|
4月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
127 1

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等