首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring面试必问:手写Spring IoC 循环依赖底层源码

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

原创
作者头像
小马哥学JAVA
发布2024-12-04 18:30:56
发布2024-12-04 18:30:56
2550
举报

2024好事发生

Java面试宝典:MongoDB实战技巧 作者:忆遂愿

https://cloud.tencent.com/developer/article/2466159?shareByChannel=link

文章对 MongoDB 知识的全面阐述,质量很高。从基本概念、Java 驱动使用、数据一致性事务处理,到数据模型设计、技术集成和存储图片优势等方面讲解详细、条理清晰,体现出作者深入的理解。推荐给各位需要的同学。

概述

在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如何解决循环依赖问题:

代码语言:javascript
复制
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:基本循环依赖

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

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

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

代码语言:javascript
复制
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在解决复杂问题时所展现出的智慧和优雅。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 功能点
  • 背景
  • 业务点
  • 底层原理
    • 三级缓存的作用
    • 解决循环依赖的流程
    • 源码实现
  • 应用实践
    • 示例1:基本循环依赖
    • 示例2:AOP代理下的循环依赖
  • 优缺点
    • 优点
    • 缺点
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档