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

相关文章
|
15天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
45 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
15天前
|
缓存 架构师 Java
图解 Spring 循环依赖,一文吃透!
Spring 循环依赖如何解决,是大厂面试高频,本文详细解析,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
图解 Spring 循环依赖,一文吃透!
|
21天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
44 9
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
21天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
31 0
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
120 5
|
2月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
2月前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
73 0