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

相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
109 2
|
22天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
108 69
|
20天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
49 21
|
27天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
13天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
25天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
2月前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
64 2
|
2月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
79 9
|
4月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
240 24