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

手写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 的名称、类型、属性等。

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。

public interface BeanFactory {BeanDefinition getBeanDefinition(String name);Object getBean(String name);
}

AbstractBeanFactory

然后,我们定义一个 AbstractBeanFactory 抽象类,实现 BeanFactory 接口的 getBean 方法,并提供一个 createBean 方法,用于创建 Bean。

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。

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,它们相互依赖。

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。

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 的实例,并验证它们是否相互依赖。

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.mzph.cn/diannao/8701.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

生产制造中刀具管理系统,帮助工厂不再频繁换刀

一、刀具管理的定义与重要性 刀具管理是指对生产过程中使用的各种刀具进行计划、采购、存储、分配、使用、监控、维修和报废等全过程的管理。刀具作为制造过程中的直接工具&#xff0c;其性能、质量和使用效率直接影响产品的加工精度、表面质量和生产效率。因此&#xff0c;建…

算法学习(6)-最短路径

目录 Floyd-Warshall算法 Dijkstra算法 Bellman-Ford算法 Bellman-Ford的队列优化 最短路径算法对比分析 Floyd-Warshall算法 现在回到问题&#xff1a;如何求任意两点之间的最短路径呢&#xff1f; 通过之前的学习&#xff0c; 我们知道通过深度或广度优先搜索可以求出两…

PostgreSQL自带的命令行工具14- pg_test_timing

PostgreSQL自带的命令行工具14- pg_test_timing 基础信息 OS版本&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a;16.2 pg软件目录&#xff1a;/home/pg16/soft pg数据目录&#xff1a;/home/pg16/data 端口&#xff1a;5777pg_test_timi…

【 npm详解:从入门到精通】

文章目录 npm详解&#xff1a;从入门到精通1. [npm](https://www.npmjs.com/)的安装2. npm的基础用法2.1 初始化项目2.2 安装依赖2.3 卸载依赖2.4 更新依赖 3. npm的高级用法3.1 运行脚本3.2 使用npm scope3.3 使用npm link 4. npm资源5. 使用npm进行依赖树分析和可视化6. npm进…

使用Nuxt.js实现服务端渲染(SSR)

Nuxt.js 是一个基于 Vue.js 的框架&#xff0c;它提供了服务器端渲染&#xff08;SSR&#xff09;和静态站点生成&#xff08;SSG&#xff09;的能力&#xff0c;使开发者能够轻松地构建高效、优雅的前端应用。Nuxt.js 集成了许多开箱即用的功能和工具&#xff0c;帮助开发者快…

天诚人脸物联网锁+网约房管理系统为智慧酒店、民宿管理赋能

随着互联网技术的发展&#xff0c;“网约房”逐渐步入受众视野&#xff0c;在改变旅客入住模式和生活方式的同时&#xff0c;为旅客旅游住宿创造了新的选择&#xff0c;也为拥有冗余房间资源的房东提供了新的营收路径。但是&#xff0c;网约房的管理问题频发&#xff0c;需要数…

springcloud alibaba微服务框架涉及的技术

一、微服务架构中核心模块及其使用技术总览 二、各模块详细说明 1、注册中心 该模块主要功能为 自动提供服务的注册与发现&#xff0c;集中式管理服务&#xff0c;让 服务调用端发现服务&#xff0c;让服务提供端注册服务&#xff0c;倘若没有注册中心&#xff0c;那客户端就…

Jenkins的原理及应用详解(三)

本系列文章简介&#xff1a; Jenkins是一种自动化构建工具&#xff0c;它的出现使得软件开发过程变得更加高效、可靠和可持续。它采用了一种事件驱动的架构&#xff0c;能够通过触发不同的事件来执行相应的构建任务和操作。Jenkins支持多种版本控制系统的集成&#xff0c;可以自…

关于react的注意事项和问题

在使用React时&#xff0c;有一些重要的注意事项和问题需要考虑。以下是一些主要的方面&#xff1a; 注意事项 组件结构和组织&#xff1a; 保持组件简单和可复用&#xff1a;将组件拆分为较小和独立的部分&#xff0c;以提高代码的可维护性和可测试性。遵循单一职责原则&…

Navicat导出表结构到Excel或Word

文章目录 sql语句复制到excel复制到Word sql语句 SELECTcols.COLUMN_NAME AS 字段,cols.COLUMN_TYPE AS 数据类型,IF(pks.CONSTRAINT_TYPE PRIMARY KEY, YES, NO) AS 是否为主键,IF(idxs.INDEX_NAME IS NOT NULL, YES, NO) AS 是否为索引,cols.IS_NULLABLE AS 是否为空,cols.…

小巧设备,大能量:探索口袋中的远程控制神器

在这个科技日新月异的时代&#xff0c;我们的生活被各种手机软件所包围。几乎每个人都有一个甚至多个手机&#xff0c;你是否也有遇到过需要远程操作自己某一台手机的场景呢&#xff1f;今天&#xff0c;我要向大家推荐一款神奇的手机远程操作神器&#xff0c;让你可以随时随地…

未授权访问:MongoDB未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章&#xff1a; 这里附上大佬…

【Chrome实用命令笔记】

文章目录 Chrome实用命令笔记1、chrome基本介绍2. 打开开发者工具&#xff08;DevTools&#xff09;方法一&#xff1a;快捷键方法二&#xff1a;右键菜单方法三&#xff1a;浏览器设置 2. 开发者工具面板Elements面板Console面板Sources面板Network面板Performance面板Memory面…

HTML Audio标签src使用base64字符

源码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>Audio src base64</title> </head> <body><audio controls><source src"data:audio/mp3;base64,//OIxAAAAAAAAAA…

SpringBoot:SpringBoot:实战项目TILAS智能学习辅助系统1.3

登录认证 需求:输入登录请求服务器判断用户的用户名和密码 //控制层 PostMapping("/login")public Result login(RequestBody Emp emp);Overridepublic Result login(Emp emp) {Emp emp1 empService.selectLogin(emp);if(emp1 null){System.out.println("用…

虚拟化技术 使用Vsphere Client管理ESXi服务器系统

使用Vsphere Client管理ESXi服务器系统 一、实验目的与要求 1.掌握使用vSphere Client管理ESXi主机 2.掌握将CentOS的安装介质ISO上传到ESXi存储 3.掌握在VMware ESXi中创建虚拟机 4.掌握在所创建的虚拟机中安装CentOS6.5操作系统 5.掌握给CentOS6.5安装VMware Tools 6.掌…

uniapp0基础编写安卓原生插件之编写安卓页面在uniapp上显示(摄像头调用)

前言 如果你对安卓插件开发部分不熟悉你可以先看uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之零基础编写安卓插件 效果 开始 dcloud_uniplugins.json {"nativePlugins": [{"hooksClass": "","plugins": [{&…

【qt】核心机制信号槽(下)

这里写目录标题 自定义的信号自定义的槽自定义的信号和槽的结合使用信号和槽的断开总结&#xff1a; 自定义的信号 信号就是一个函数声明 前面咱们都用的qt组件自带的信号&#xff0c;接下来我们自己写一个信号。 信号只需要在前面加一个signals即可 这个函数不需要实现 参数传…

Grounded-Segment-Anything实现自动文本标注

项目地址&#xff1a;IDEA-Research/Grounded-Segment-Anything: Grounded-SAM: Marrying Grounding-DINO with Segment Anything & Stable Diffusion & Recognize Anything - Automatically Detect , Segment and Generate Anything (github.com) demo地址&#xff1a…

邦芒面试:面试时如何有效发挥口才

面试是获取心仪职位的关键一关&#xff0c;良好的口才表现能让你在众多求职者中脱颖而出。以下是一些面试时发挥口才的建议&#xff0c;帮助你成为面试中的优胜者&#xff1a; 1、深思熟虑&#xff0c;言之有物 在回答问题之前&#xff0c;先花时间思考&#xff0c;确保你的回…