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

news/2024/9/23 15:32:49/

手写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/news/1458434.html

相关文章

sql注入手注语句

?id100 union select 1,database(),3-- asd ​ ?id100 union select 1,table_name,3 from information_schema.tables where table_schemasecurity limit 0,1-- asd ?id100 union select 1,group_concat(table_name),3 from information_schema.tables where table_schemada…

第1个数据库:编号,文本,时间,

写一个数据库 编号 文本 时间1 第一个文本 有100万条数据 -- 创建一个名为texts的表格来存储数据 CREATE TABLE texts ( id INTEGER PRIMARY KEY, text TEXT, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);-- 插入数据INSERT INTO texts (text) VALUES (第一个文…

springboot项目中引入Xxl-Job并部署和使用

目录 模块划分 配置调度中心 配置执行器 添加执行器 写一个简单的定时任务 XxlJobHelper xxl-job是分布式任务调度平台&#xff0c;部署为独立的调度服务平台 github地址&#xff1a;xuxueli/xxl-job: A distributed task scheduling framework.&#xff08;分布式任务调度…

走进SVG:不懂就OUT了!

在当今的数字世界里&#xff0c;图形设计和网页设计是我们生活的重要组成部分。无论是滚动你的社交媒体页面&#xff0c;还是浏览你最喜欢的网站&#xff0c;你都会接触到各种图形和图像。其中&#xff0c;有一种特殊的文件格式被广泛使用&#xff0c;它可以给你带来前所未有的…

使用凌鲨建立软件研发技能学习小组

凌鲨(OpenLinkSaas)的团队功能除了提供论坛功能&#xff0c;还能记录团队成员的成长记录。 使用方法 打开团队功能 团队功能在默认情况下是关闭的&#xff0c;你可以在登录后打开团队功能开关。 创建学习团队 日报/周报/个人目标一般是企业团队需要&#xff0c;建议关闭。 …

# 从浅入深 学习 SpringCloud 微服务架构(八)Sentinel(2)

从浅入深 学习 SpringCloud 微服务架构&#xff08;八&#xff09;Sentinel&#xff08;2&#xff09; 一、sentinel&#xff1a;通用资源保护 1、Rest 实现熔断 Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护, 在构造 RestTemplate…

AI换脸原理(4)——人脸对齐(关键点检测)参考文献2DFAN:代码解析

注意,本文属于人脸关键点检测步骤的论文,虽然也在人脸对齐的范畴下。 1、介绍 在本文中,重点介绍了以下几项创新性的成果,旨在为人脸关键点检测领域带来新的突破。 首先,成功构建了一个卓越的2D人脸关键点检测基线模型。这一模型不仅集成了目前最优的关键点检测网络结构,…

Socks5代理IP可以运用到哪些应用场景?

在网络技术快速发展的今天&#xff0c;Socks5代理和HTTP代理已成为两种常见的代理服务类型。它们在很多方面都有应用&#xff0c;但Socks5代理以其独特的特点和更加广泛的应用场景而受到青睐。在深入探讨Socks5代理的应用场景之前&#xff0c;让我们首先区分Socks5代理和HTTP代…