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

devtools/2024/9/23 14:22:57/

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

前言

Spring Ioc 是一个非常重要的框架,它的核心是依赖注入(DI)和控制反转(IOC)。在使用 Spring Ioc 的过程中,我们经常会遇到循环依赖的问题。本文将介绍手写 Spring Ioc 循环依赖的底层源码剖析。

什么是循环依赖

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

Spring Ioc 循环依赖的解决方案

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

  1. 提前暴露半成品 Bean
  2. 使用三级缓存

提前暴露半成品 Bean

提前暴露半成品 Bean 是指在创建 Bean 的过程中,如果发现循环依赖,就将半成品 Bean 提前暴露出来,以便其他 Bean 可以使用。等到所有 Bean 都创建完成后,再将半成品 Bean 完成创建。

使用三级缓存

使用三级缓存是指在创建 Bean 的过程中,如果发现循环依赖,就将正在创建的 Bean 放入三级缓存中。等到所有 Bean 都创建完成后,再从三级缓存中取出 Bean,完成创建。

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

下面我们来手写一个简单的 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<>();@Overridepublic 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);}
}

DefaultBeanFactory

最后,我们定义一个 DefaultBeanFactory 类,继承 AbstractBeanFactory 抽象类,实现 BeanFactory 接口的 getBeanDefinition 方法,并提供一个 registerBeanDefinition 方法, 用于注册 BeanDefinition。

java">public class DefaultBeanFactory extends AbstractBeanFactory {@Overridepublic BeanDefinition getBeanDefinition(String name) {return super.beanDefinitions.get(name);}public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {super.addBeanDefinition(name, beanDefinition);}
}

测试

现在,我们来测试一下我们手写的 Spring Ioc 容器是否能够解决循环依赖的问题。

首先,我们定义两个类 A 和 B,它们相互依赖。

java">public class A {private B b;public void setB(B b) {this.b = b;}
}public class B {private A a;public void setA(A a) {this.a = a;}
}

然后,我们在容器中注册这两个类的 BeanDefinition。

java">DefaultBeanFactory beanFactory = new DefaultBeanFactory();BeanDefinition aBeanDefinition = new BeanDefinition("a", A.class);
aBeanDefinition.setProperty("b", new ObjectFactory<B>() {@Overridepublic B getObject() {return (B) beanFactory.getBean("b");}
});
beanFactory.registerBeanDefinition("a", aBeanDefinition);BeanDefinition bBeanDefinition = new BeanDefinition("b", B.class);
bBeanDefinition.setProperty("a", new ObjectFactory<A>() {@Overridepublic A getObject() {return (A) beanFactory.getBean("a");}
});
beanFactory.registerBeanDefinition("b", bBeanDefinition);

最后,我们从容器中获取 A 和 B 的实例,并验证它们是否相互依赖。

java">A a = (A) beanFactory.getBean("a");
B b = (B) beanFactory.getBean("b");assert a != null;
assert b != null;
assert a.getB() == b;
assert b.getA() == a;

测试通过,我们手写的 Spring Ioc 容器能够解决循环依赖的问题。

总结

本文介绍了手写 Spring Ioc 循环依赖的底层源码剖析。通过实现一个简单的 Spring Ioc 容器,我们了解了 Spring Ioc 解决循环依赖的两种方案,并掌握了手写 Spring Ioc 容器的方法。


http://www.ppmy.cn/devtools/40012.html

相关文章

iOS 安装cocoapds

注意 CocoaPods安装是基于ruby环境的&#xff0c;所以要安装CocoaPods先要安装Ruby环境&#xff0c;国内不能直接安装&#xff0c;只能通过VPN或淘宝的Ruby镜像来访问。 安装过程 gem sources --remove https://rubygems.org/ ** (注意是两个“-”&#xff0c;否则会移除失败) …

PHP类和对象扩展[构造函数,析构函数]

构造函数 特性&#xff1a;实例化对象时调用 作用&#xff1a;通常用于 初始化属性值 或 执行启动时所需的操作 用法&#xff1a; function __construct($parameter){ $this->attribute $parameter; echo 对象已创建并&#xff0c;初始化attribute属性值为parameter; …

苹果平板HOME键成历史,全面屏时代到来?2024平板电脑市场趋势分析

近期苹果公司在“放飞吧”发布会上推出了新款iPad Pro和iPad Air平板电脑&#xff0c;并下架了最后一款带有实体Home按键的iPad 9。这一变化标志着Home键在苹果iPad产品线中成为了历史&#xff0c;引起了不少网友的怀念和感慨。 与此同时&#xff0c;今年3月线上平板电脑市场迎…

Git === Git概述 Git安装

第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于Subversion…

关于Matplotlib如何在网页中使用?

目录 一、如何在网页中使用matplotlib 二、如何使用mpld3在网页中显示图表 三、如何使用matplotlibflask在网页中显示图表 一、如何在网页中使用matplotlib Matplotlib是Python中一个非常流行的可视化库。然而&#xff0c;Matplotlib主要是为桌面应用程序设计的&#xff0c;…

【Chisel】chisel中怎么处理类似verilog的可变位宽和parameter

在 Chisel 中处理可变位宽和参数的方式与 Verilog 有一些不同&#xff0c;因为 Chisel 是建立在 Scala 语言之上的。以下是如何在 Chisel 中处理这些概念的方法&#xff1a; 参数化&#xff08;Parameters&#xff09; 在 Chisel 中&#xff0c;参数化是通过在模块构造函数中定…

物联网到底物联了个啥?——青创智通

工业物联网解决方案-工业IOT-青创智通 物联网&#xff0c;这个听起来似乎颇具科技感和未来感的词汇&#xff0c;其实早已悄然渗透到我们生活的方方面面。从智能家居到智慧城市&#xff0c;从工业自动化到医疗健康&#xff0c;物联网技术正在以其独特的魅力改变着我们的生活方式…

java实现音频、视频离开页面存储收听记录、观看记录

说明:本文是既《SpringBoot+thymeleaf完成视频记忆播放功能》的续写,如有兴趣可点击链接查看。 一、功能背景描述说明: 1、在手机浏览器离开时将看到的视频记录、视频时长和音频的收听记录、收听时长存入数据库记录; 2、用户通过将观看、收听记录导出,能够看到是谁看了哪些…