手写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或循环依赖有更深入的问题,欢迎留言讨论。

相关文章
|
4天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
3天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
20天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
20天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
26天前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。
|
26天前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
19 1
|
28天前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
20 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
15天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
43 0
|
20天前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
35 0
|
20天前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
62 0