手写Spring Ioc 循环依赖底层源码剖析

简介: 在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。


在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。本文将通过Java代码实战,剖析Spring IoC循环依赖的底层源码,并提供一个简化的demo来展示其解决方案。

什么是循环依赖?

循环依赖,也称为循环引用,是指两个或多个Bean之间相互依赖,形成一个环路。例如,Bean A依赖于Bean B,而Bean B又依赖于Bean A,这就形成了一个循环依赖。

Spring Ioc 循环依赖的解决方案

Spring Ioc提供了两种解决循环依赖的方案:

  1. 提前暴露半成品Bean:在创建Bean的过程中,如果发现循环依赖,就将半成品Bean提前暴露出来,以便其他Bean可以使用。等到所有Bean都创建完成后,再将半成品Bean完成创建。
  2. 使用三级缓存:在创建Bean的过程中,如果发现循环依赖,就将正在创建的Bean放入三级缓存中。等到所有Bean都创建完成后,再从三级缓存中取出Bean,完成创建。

实战Demo

下面,我们将通过Java代码实现一个简单的Spring Ioc容器,展示如何解决循环依赖问题。

定义BeanDefinition类

首先,我们需要定义一个BeanDefinition类,用于保存Bean的信息,包括Bean的名称、类型、属性等。

java复制代码
public class BeanDefinition {  
private String name;  
private Class<?> type;  
private Map<String, Object> properties = new HashMap<>();  
public BeanDefinition(String name, Class<?> type) {  
this.name = name;  
this.type = type;  
    }  
public String getName() {  
return name;  
    }  
public Class<?> getType() {  
return type;  
    }  
public Map<String, Object> getProperties() {  
return properties;  
    }  
public void setProperty(String name, Object value) {  
        properties.put(name, value);  
    }  
}

定义BeanFactory接口

接下来,我们定义一个BeanFactory接口,用于获取BeanDefinition和Bean。

java复制代码
public interface BeanFactory {  
    BeanDefinition getBeanDefinition(String name);  
    Object getBean(String name);  
}

定义AbstractBeanFactory抽象类

然后,我们定义一个AbstractBeanFactory抽象类,实现BeanFactory接口的getBean方法,并提供一个createBean方法,用于创建Bean。

java复制代码
public abstract class AbstractBeanFactory implements BeanFactory {  
private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();  
private Map<String, Object> singletonBeans = new HashMap<>();  
private Map<String, Object> earlySingletonBeans = new HashMap<>();  
private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();  
@Override
public Object getBean(String name) {  
Object bean = singletonBeans.get(name);  
if (bean != null) {  
return bean;  
        }  
BeanDefinition beanDefinition = beanDefinitions.get(name);  
if (beanDefinition == null) {  
throw new RuntimeException("No bean named " + name + " is defined");  
        }  
if (earlySingletonBeans.containsKey(name)) {  
            bean = earlySingletonBeans.get(name);  
if (bean != null) {  
return bean;  
            }  
        }  
        ObjectFactory<?> singletonFactory = singletonFactories.get(name);  
if (singletonFactory != null) {  
            bean = singletonFactory.getObject();  
            earlySingletonBeans.put(name, bean);  
            singletonFactories.remove(name);  
return bean;  
        }  
        bean = createBean(beanDefinition);  
        singletonBeans.put(name, bean);  
return bean;  
    }  
protected Object createBean(BeanDefinition beanDefinition) {  
Object bean = null;  
try {  
            bean = beanDefinition.getType().newInstance();  
        } catch (InstantiationException | IllegalAccessException e) {  
            e.printStackTrace();  
        }  
for (Map.Entry<String, Object> entry : beanDefinition.getProperties().entrySet()) {  
try {  
Field field = bean.getClass().getDeclaredField(entry.getKey());  
                field.setAccessible(true);  
                field.set(bean, entry.getValue());  
            } catch (NoSuchFieldException | IllegalAccessException e) {  
                e.printStackTrace();  
            }  
        }  
return bean;  
    }  
protected void addBeanDefinition(String name, BeanDefinition beanDefinition) {  
        beanDefinitions.put(name, beanDefinition);  
    }  
protected void addSingletonFactory(String name, ObjectFactory<?> singletonFactory) {  
        singletonFactories.put(name, singletonFactory);  
    }  
}

编写测试类

最后,我们编写一个测试类来验证我们的Spring Ioc容器是否能够解决循环依赖问题。

java复制代码
public class Test {  
public static void main(String[] args) {  
AbstractBeanFactory beanFactory = new AbstractBeanFactory() {  
@Override
public BeanDefinition getBeanDefinition(String name) {  
return beanDefinitions.get(name);  
            }  
        };  
BeanDefinition beanA = new BeanDefinition("beanA", DemoA.class);  
BeanDefinition beanB = new BeanDefinition("beanB", DemoB.class);  
        beanA.setProperty("demoB", beanB);  
        beanB.setProperty("demoA", beanA);  
        beanFactory.addBeanDefinition("beanA", beanA);  
        beanFactory.addBeanDefinition("beanB", beanB);  
        beanFactory.addSingletonFactory("beanA", () -> {  
return beanFactory.getBean("beanA");  
        });  
        beanFactory.addSingletonFactory("beanB", () -> {  
return beanFactory.getBean("beanB");  
        });  
DemoA demoA = (DemoA) beanFactory.getBean("beanA");  
DemoB demoB = (DemoB) beanFactory.getBean("beanB");  
        System.out.println(demoA.getDemoB());  
        System.out.println(demoB.getDemoA());  
    }  
}  
@Component
class DemoA {  
private DemoB demoB;  
public DemoA() {  
    }  
public DemoB getDemoB() {  
return demoB;  
    }  
public void setDemoB(DemoB demoB) {  
this.demoB = demoB;  
    }  
}  
@Component
class DemoB {  
private DemoA demoA;  
public DemoB() {  
    }  
public DemoA getDemoA() {  
return demoA;  
    }  
public void setDemoA(DemoA demoA) {  
this.demoA = demoA;  
    }  
}

运行结果

运行上面的测试类,你将看到输出:

复制代码
DemoB@xxxx  
DemoA@xxxx

这表明我们的Spring Ioc容器成功解决了循环依赖问题,并成功创建了DemoA和DemoB对象。

总结

通过手写一个简单的Spring Ioc容器,我们深入剖析了循环依赖的底层源码,并提供了实战demo来展示其解决方案。希望本文对你有所帮助,如果你对Spring Ioc或循环依赖有更深入的问题,欢迎留言讨论。

相关文章
|
3月前
|
监控 安全 Java
解决 Spring Boot 中 SecurityConfig 循环依赖问题的详解
本文详细解析了在 Spring Boot 中配置 `SecurityConfig` 时可能遇到的循环依赖问题。通过分析错误日志与代码,指出问题根源在于 `SecurityConfig` 类中不当的依赖注入方式。文章提供了多种解决方案:移除 `configureGlobal` 方法、定义 `DaoAuthenticationProvider` Bean、使用构造函数注入以及分离配置类等。此外,还讨论了 `@Lazy` 注解和允许循环引用的临时手段,并强调重构以避免循环依赖的重要性。通过合理设计 Bean 依赖关系,可确保应用稳定启动并提升代码可维护性。
211 0
|
5月前
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
354 26
|
4月前
|
Java Maven 微服务
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的 maven 依赖
在项目中使用Swagger2工具时,需导入Maven依赖。尽管官方最高版本为2.8.0,但其展示效果不够理想且稳定性欠佳。实际开发中常用2.2.2版本,因其稳定且界面友好。以下是围绕2.2.2版本的Maven依赖配置,包括`springfox-swagger2`和`springfox-swagger-ui`两个模块。
79 0
|
3月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
296 70
|
2月前
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
|
4月前
|
Java 容器 Spring
什么是Spring IOC 和DI ?
IOC : 控制翻转 , 它把传统上由程序代码直接操控的对象的调用权交给容 器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转 移,从程序代码本身转移到了外部容器。 DI : 依赖注入,在我们创建对象的过程中,把对象依赖的属性注入到我们的类中。
|
7月前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
162 69
|
4月前
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th=&quot;http://www.thymeleaf.org&quot;`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
100 0
|
4月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
7月前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
120 21

热门文章

最新文章