Spring面试必问:手写Spring IoC 循环依赖底层源码剖析

简介: 在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。

概述

在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。

功能点

  1. 循环依赖的定义:两个或多个Bean相互依赖,形成闭环。
  2. Spring的解决方案:通过三级缓存机制解决循环依赖问题。
  3. 源码实现:手写核心代码,展示Spring如何解决循环依赖。

背景

在Spring中,获取一个单例Bean需要经过从缓存中查询、创建对象、依赖注入、将对象放入单例Bean缓存等步骤。当没有循环依赖时,这个流程运行得很好。但一旦出现循环依赖,就会出现问题。例如,Bean C在依赖注入Bean B时,缓存中没有Bean B(因为此时Bean B还没有完成创建),只能创建Bean B,从而导致重复创建(因为此时Bean B已经在创建中)。

业务点

Spring通过引入三级缓存机制来解决循环依赖问题:

  1. 一级缓存(singletonObjects):存储创建完毕的单例Bean。
  2. 二级缓存(earlySingletonObjects):存储已实例化但未完成创建的单例Bean。
  3. 三级缓存(singletonFactories):存储单例Bean对应的ObjectFactory,用于生成Bean实例。

底层原理

三级缓存的作用

  • singletonObjects:缓存完整的Bean对象。
  • earlySingletonObjects:缓存早期的Bean对象,即Bean的生命周期还未完全走完,但已经可以提前暴露给其他Bean使用。
  • singletonFactories:缓存ObjectFactory,用于在需要时创建Bean实例。

解决循环依赖的流程

  1. 创建Bean实例:通过反射调用构造方法创建Bean的原始对象。
  2. 判断是否存在循环依赖:如果Bean正在创建中,则存在循环依赖。
  3. 将Bean实例放入三级缓存:创建一个ObjectFactory并放入singletonFactories中,ObjectFactory的getObject方法会返回Bean的实例。
  4. 依赖注入:在依赖注入过程中,如果发现依赖的Bean不存在于一级缓存和二级缓存中,则从三级缓存中获取ObjectFactory并创建Bean实例。
  5. 将Bean实例放入二级缓存:如果Bean实例是通过ObjectFactory创建的,则将其放入earlySingletonObjects中。
  6. 完成Bean的生命周期:包括属性填充、AOP代理生成等步骤。
  7. 将Bean实例放入一级缓存:最终将完整的Bean对象放入singletonObjects中。

源码实现

下面是一个简化的源码实现,用于展示Spring如何解决循环依赖问题:

java复制代码
public class DefaultSingletonBeanRegistry {
// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 判断Bean是否正在创建中
private final Set<String> singletonsCurrentlyInCreation = new HashSet<>(16);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
                }
            }
        }
return singletonObject;
    }
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
    }
public void beforeSingletonCreation(String beanName) {
this.singletonsCurrentlyInCreation.add(beanName);
    }
public void afterSingletonCreation(String beanName) {
this.singletonsCurrentlyInCreation.remove(beanName);
this.singletonObjects.put(beanName, this.earlySingletonObjects.remove(beanName));
    }
}

应用实践

示例1:基本循环依赖

java复制代码
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}

在这个例子中,A和B相互依赖,Spring通过三级缓存机制能够成功解决循环依赖问题。

示例2:AOP代理下的循环依赖

java复制代码
@Component
public class A {
@Autowired
private B b;
}
@Component
@Aspect
public class B {
@Autowired
private A a;
}

在这个例子中,B是一个切面,Spring会在创建B的代理对象时处理循环依赖问题。

优缺点

优点

  • 简化依赖管理:通过IoC容器管理Bean的依赖关系,降低了代码耦合度。
  • 提高可测试性:方便使用Mock对象进行单元测试。
  • 解决循环依赖:通过三级缓存机制有效解决了循环依赖问题。

缺点

  • 性能损耗:三级缓存机制增加了Bean的创建成本。
  • 复杂性增加:对于开发者来说,理解三级缓存机制需要一定的学习成本。

总结

通过本文的深入剖析,相信你对Spring IoC循环依赖的底层源码有了全新的认识。Spring通过三级缓存机制巧妙地解决了循环依赖问题,使得开发者能够更加方便地管理Bean的依赖关系。同时,我们也看到了Spring在解决复杂问题时所展现出的智慧和优雅。

相关文章
|
21天前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
7月前
|
监控 安全 Java
解决 Spring Boot 中 SecurityConfig 循环依赖问题的详解
本文详细解析了在 Spring Boot 中配置 `SecurityConfig` 时可能遇到的循环依赖问题。通过分析错误日志与代码,指出问题根源在于 `SecurityConfig` 类中不当的依赖注入方式。文章提供了多种解决方案:移除 `configureGlobal` 方法、定义 `DaoAuthenticationProvider` Bean、使用构造函数注入以及分离配置类等。此外,还讨论了 `@Lazy` 注解和允许循环引用的临时手段,并强调重构以避免循环依赖的重要性。通过合理设计 Bean 依赖关系,可确保应用稳定启动并提升代码可维护性。
559 0
|
3月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
7月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
425 70
|
5月前
|
XML 人工智能 Java
Spring IOC 到底是什么?
IOC(控制反转)是一种设计思想,主要用于解耦代码,简化依赖管理。其核心是将对象的创建和管理交给容器处理,而非由程序直接硬编码实现。通过IOC,开发者无需手动new对象,而是由框架负责实例化、装配和管理依赖对象。常见应用如Spring框架中的BeanFactory和ApplicationContext,它们实现了依赖注入和动态管理功能,提升了代码的灵活性与可维护性。
176 1
|
5月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
230 3
|
XML Java 数据格式
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
|
6月前
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
|
8月前
|
Java 容器 Spring
什么是Spring IOC 和DI ?
IOC : 控制翻转 , 它把传统上由程序代码直接操控的对象的调用权交给容 器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转 移,从程序代码本身转移到了外部容器。 DI : 依赖注入,在我们创建对象的过程中,把对象依赖的属性注入到我们的类中。

热门文章

最新文章