手写Spring:第3章-实现Bean的定义、注册、获取
文章目录
- 一、目标:实现Bean的定义、注册、获取
- 二、设计:实现Bean的定义、注册、获取
- 三、实现:实现Bean的定义、注册、获取
- 3.1 工程结构
- 3.2 实现Bean的定义、注册、获取类图
- 3.3 定义Bean异常
- 3.4 BeanDefinition定义和注册
- 3.4.1 BeanDefinition定义
- 3.4.2 BennDefinition定义注册接口
- 3.5 单例注册接口定义和实现
- 3.5.1 单例注册接口
- 3.5.2 单例注册接口的实现
- 3.6 抽象类定义模板方法
- 3.6.1 Bean工厂接口
- 3.6.2 抽象Bean工厂基类,定义模板方法
- 3.6.3 实例化Bean类
- 3.6.4 核心类:默认的Bean工厂实现
- 四、测试:实现Bean的定义、注册、获取
- 五、总结:实现Bean的定义、注册、获取
一、目标:实现Bean的定义、注册、获取
💡 实现 Bean 容器关于 Bean 对象的注册和获取?
- 把 Bean 的创建交给容器,而不是我们在调用时候传递一个实例化好的 Bean 对象。
- 另外还需要考虑单例对象,在对象的二次获取时是可以从内存中获取对象的。
- 此外不仅要实现功能还需要完善基础容器框架的类结构体,否则将来就很难扩容进去其他的功能。
二、设计:实现Bean的定义、注册、获取
💡 实现Bean的定义、注册、获取?
- 首先是在 Bean 注册的时候只注册一个类信息,而不会直接把实例化信息注册到 Spring 容器中。
- 那么就需要修改 BeanDefinition 中的属性
Object为Class,接下来就是在获取 Bean 对象时需要处理 Bean 对象的实例化操作以及判断当前单例对象在容器中是否已经缓存起来。

- 首先需要定义 BeanFactory 这样一个 Bean 工厂,提供 Bean 的获取方法
getBean(String name),之后这个 Bean 工厂接口由抽象类 AbstractBeanFactory 实现。- 这样用 模板模式 的设计方式,可以统一设计通用核心方法的调用逻辑和标准定义,也就很好的控制了后续的实现者不用关心调用逻辑。
- 按照统一方式执行。那么类型的继承者只需要关心具体方法的逻辑实现即可。
- 在继承抽象类 AbstractBeanFactory 后的 AbstractAutowireCapableBeanFactory 就可以实现相应的抽象方法了。
- 因为 AbstractAutowireCapableBeanFactory 本身也是一个抽象类,所以它只会实现属于自己的抽象方法,其他抽象方法由继承 AbstractAutowireCapableBeanFactory 的类实现。
- 这里就体现了类实现过程中的各司其职,你只需要关心属于你的内容,不是你的内容,不要参与。
- 另外还有块重要的知识点,就是关于单例 SingletonBeanRegistry 的接口定义实现,而 DefaultSingletonBeanRegistry 对接口实现后,会被抽象类 AbstractBeanFactory 继承。
- 现在 AbstractBeanFactory 就是一个非常完整且强大的抽象类了,也能非常好的体现出它对模板模式的抽象定义。
三、实现:实现Bean的定义、注册、获取
3.1 工程结构
spring-step-02
|-src|-main| |-java| |-com.lino.springframework| |-factory| | |-config| | | |-BeanDefinition.java| | | |-SingletonBeanRegistry.java| | |-support| | | |-AbstractAutowireCapableBeanFactory.java| | | |-AbstractBeabFactory.java| | | |-BeanDefinitionRegistry.java| | | |-DefaultListableBeanFactory.java| | | |-DefaultSingletonBeanRegistry.java| | |-BeanFactory.java| |-BeansException.java|-test|-java|-com.lino.springframework.test|-bean| |-UserService.java|-ApiTest.java
3.2 实现Bean的定义、注册、获取类图

- Spring Bean 容器的功能实现,已经具备了一定的设计复杂性,这些复杂的类关系设计在各个接口定义和实现以及在抽象类继承中都有所体现。
- BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的
getBean方法。 - 而 AbstractBeanFactory 又继承实现了 SingletonBeanRegistry 的 DefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例 Bean 的注册功能。
- AbstractBeanFactory 中又定义了两个抽象方法:
getBeanDefinition(String beanName)、createBean(String beanName, BeanDefinition beanDefinition),这两个抽象方法分别由 DefaultListableBeanFactory、AbstractAutowireCapableBeanFactory 实现。 - 最终 DefaultListableBeanFactory 还会继承抽象类 AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的
createBean方法了。
- BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的
- 所有的实现都以职责划分、共性分离以及调用关系定义为标准搭建的类关系。
3.3 定义Bean异常
BeansException.java
package com.lino.springframework;/*** @description: 定义 Bean 异常*/
public class BeansException extends RuntimeException {public BeansException(String msg) {super(msg);}public BeansException(String msg, Throwable cause) {super(msg, cause);}
}
3.4 BeanDefinition定义和注册
3.4.1 BeanDefinition定义
BeanDefinition.java
package com.lino.springframework.factory.config;/*** @description: Bean 对象信息定义*/
public class BeanDefinition {/*** bean对象*/private Class beanClass;public BeanDefinition(Class beanClass) {this.beanClass = beanClass;}public Class getBeanClass() {return beanClass;}public void setBeanClass(Class beanClass) {this.beanClass = beanClass;}
}
- 在 Bean 定义类中把
Object替换为Class,这样就可以把 Bean 的实例化操作放到容器中处理了。
3.4.2 BennDefinition定义注册接口
BeanDefinitionRegistry.java
package com.lino.springframework.factory.support;import com.lino.springframework.factory.config.BeanDefinition;/*** @description: Bean 定义注册接口*/
public interface BeanDefinitionRegistry {/*** 向注册表中注册 BeanDefinition** @param beanName Bean 名称* @param beanDefinition Bean 定义*/void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}
- Bean 定义注册接口只有一个接口方法,就是向注册表中注册 BeanDefinition。
3.5 单例注册接口定义和实现
3.5.1 单例注册接口
SingletonBeanRegistry
package com.lino.springframework.factory.config;/*** @description: 单例 Bean 注册表*/
public interface SingletonBeanRegistry {/*** 返回在给定名称下注册的(原始)单例对象** @param beanName 要查找的bean的名称* @return 返回注册的单例对象*/Object getSingleton(String beanName);/*** 注册单例对象** @param beanName Bean 对象名称* @param singletonObject Bean 对象*/void registerSingletonBean(String beanName, Object singletonObject);
}
- 这个类比较简单,主要是定义了一个获取单例对象和注册单例对象的接口。
3.5.2 单例注册接口的实现
DefaultSingletonBeanRegistry.java
package com.lino.springframework.factory.support;import com.lino.springframework.factory.config.SingletonBeanRegistry;
import java.util.HashMap;
import java.util.Map;/*** @description: 通用的注册表实现*/
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {private Map<String, Object> singletonObjects = new HashMap<>();@Overridepublic Object getSingleton(String beanName) {return singletonObjects.get(beanName);}@Overridepublic void registerSingletonBean(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject);}
}
- 在 DefaultSingletonBeanRegistry 主要实现
getSingleton方法和registerSingletonBean方法。
3.6 抽象类定义模板方法
3.6.1 Bean工厂接口
BeanFactory.java
package com.lino.springframework.factory;import com.lino.springframework.BeansException;
import com.lino.springframework.factory.config.BeanDefinition;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @description: 定义 Bean 工厂接口*/
public interface BeanFactory {/*** 返回 Bean 的实例对象** @param name 要检索的bean的名称* @return 实例化的 Bean 对象* @throws BeansException 不能获取 Bean 对象,抛出异常*/Object getBean(String name) throws BeansException;
}
3.6.2 抽象Bean工厂基类,定义模板方法
AbstractBeanFactory.java
package com.lino.springframework.factory.support;import com.lino.springframework.BeansException;
import com.lino.springframework.factory.BeanFactory;
import com.lino.springframework.factory.config.BeanDefinition;/*** @description: 抽象的 Bean 工厂基类,定义模板方法*/
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {@Overridepublic Object getBean(String name) throws BeansException {Object bean = getSingleton(name);if (bean != null) {return bean;}BeanDefinition beanDefinition = getBeanDefinition(name);return createBean(name, beanDefinition);}/*** 获取 Bean 对象** @param beanName 要检索的bean的名称* @return Bean 对象*/protected abstract BeanDefinition getBeanDefinition(String beanName);/*** 创建Bean对象** @param beanName 要检索的bean的名称* @param beanDefinition Bean对象* @return 实例化的Bean对象*/protected abstract Object createBean(String beanName, BeanDefinition beanDefinition);
}
- AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry,也就具备了使用单例注册类方法。
- 接下来很重要的一点是关于接口 BeanFactory 的实现。
- 在方法
getBean的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作。 - 那么
getBean并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。
- 在方法
- 后续继承抽象类 AbstractBeanFactory 的类有两个。
- 包括:
AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory,这两个类分别做了相应的实现处理。
- 包括:
3.6.3 实例化Bean类
AbstractAutowireCapableBeanFactory.java
package com.lino.springframework.factory.support;import com.lino.springframework.BeansException;
import com.lino.springframework.factory.config.BeanDefinition;/*** @description: 实现默认bean创建的抽象bean工厂超类*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {@Overrideprotected Object createBean(String beanName, BeanDefinition beanDefinition) {Object bean = null;try {bean = beanDefinition.getBeanClass().newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new BeansException("Instantiation of bean failed", e);}registerSingletonBean(beanName, bean);return bean;}
}
- 在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作
newInstance。 - 在处理完 Bean 对象的实例化后,直接调用
registerSingletonBean方法存放到单例对象的缓存中去。
3.6.4 核心类:默认的Bean工厂实现
DefaultListableBeanFactory.java
package com.lino.springframework.factory.support;import com.lino.springframework.BeansException;
import com.lino.springframework.factory.config.BeanDefinition;
import java.util.HashMap;
import java.util.Map;/*** @description: 默认的Bean工厂实现类*/
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();@Overrideprotected BeanDefinition getBeanDefinition(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new BeansException("No bean named '" + beanName + "' is defined");}return beanDefinition;}@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {beanDefinitionMap.put(beanName, beanDefinition);}
}
- DefaultListableBeanFactory 在 Spring 的源码中也是一个非常核心的类。
- DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactory 和 AbstractBeanFactory 等一连串的功能实现。
- 除此之外这个类还实现了接口 BeanDefinitionRegistry 中的
registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法。- 还会看到一个
getBeanDefinition的实现,这个方法是抽象类 AbstractBeanFactory 中定义的抽象方法。现在注册 Bean 定义与获取 Bean 定义就可以同时使用了。 - 接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的
beanDefinitionMap里。
- 还会看到一个
四、测试:实现Bean的定义、注册、获取
ApiTest.java
@Test
public void test_BeanFactory() {// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2.注册beanBeanDefinition beanDefinition = new BeanDefinition(UserService.class);beanFactory.registerBeanDefinition("userService", beanDefinition);// 3.第一次获取beanUserService userService = (UserService) beanFactory.getBean("userService");userService.queryUserInfo();// 3.第二次获取beanUserService userService_singleton = (UserService) beanFactory.getBean("userService");userService_singleton.queryUserInfo();
}
- 在此次的单元测试中处理包括:
Bean 工厂、注册 Bean、获取 Bean三个步骤,还额外增加了一次对象的获取和调用。- 这里主要测试验证单例对象是否正确的存放到了缓存中。
- 此时,我们把
UserService.class传递给了 BeanDefinition。
测试结果
查询用户信息查询用户信息

- 这里会有两次测试信息,一次是获取 Bean 时直接创建的对象,另外一次是从缓存中获取的实例化对象。
- 此外从调试的截图中看出第二次获取单例对象,是从内存中获取的。
五、总结:实现Bean的定义、注册、获取
- 在 Spring Bean 容器的实现类中要重点关注类之间的职责和关系,几乎所有的程序功能设计都离不开接口、抽象类、实现、继承。
- 而这些不同特性类的使用就可以非常好的隔离开类的功能职责和作用范围。
相关文章:
手写Spring:第3章-实现Bean的定义、注册、获取
文章目录 一、目标:实现Bean的定义、注册、获取二、设计:实现Bean的定义、注册、获取三、实现:实现Bean的定义、注册、获取3.1 工程结构3.2 实现Bean的定义、注册、获取类图3.3 定义Bean异常3.4 BeanDefinition定义和注册3.4.1 BeanDefinitio…...
这些国外客户真直接
最近在某平台上遇到的客户,很大一部分都是非英语国家的客户,然而他们也有很多共性的习惯。 第一种:直接表达自己对这个产品感兴趣,然后接下来就没有下文了,而之所以可以看得懂,则是借助平台本身的翻译系统&…...
使用Apache Doris自动同步整个 MySQL/Oracle 数据库进行数据分析
Flink-Doris-Connector 1.4.0 允许用户一步将包含数千个表的整个数据库(MySQL或Oracle )摄取到Apache Doris(一种实时分析数据库)中。 通过内置的Flink CDC,连接器可以直接将上游源的表模式和数据同步到Apache Doris&…...
【1++的数据结构】之哈希(一)
👍作者主页:进击的1 🤩 专栏链接:【1的数据结构】 文章目录 一,什么是哈希?二,哈希冲突哈希函数哈希冲突解决 unordered_map与unordered_set 一,什么是哈希? 首先我们要…...
【网络编程】深入了解UDP协议:快速数据传输的利器
(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮࿰…...
WordPress(5)在主题中添加文章字数和预计阅读时间
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 样式图一、添加位置二、找到主题文件样式图 提示:以下是本篇文章正文内容,下面案例可供参考 一、添加位置 二、找到主题文件 在主题目录下functions.php文件把下面的代码添加进去: // 文章字数…...
STM32WB55开发(1)----套件概述
STM32WB55开发----1.套件概述 所用器件视频教学样品申请优势支持协议系统控制和生态系统访问功能示意图系统框图跳线设置开发板原理图 所用器件 所使用的器件是我们自行设计的开发板,该开发板是基于 STM32WB55 系列微控制器所构建。STM32WBXX_VFQFPN68 不仅是一款评…...
CUDA相关知识科普
显卡 显卡(Video card,Graphics card)全称显示接口卡,又称显示适配器,是计算机最基本配置、最重要的配件之一。就像电脑联网需要网卡,主机里的数据要显示在屏幕上就需要显卡。因此,显卡是电脑进…...
恒运资本:总市值和总资产区别?
总市值和总财物是财政术语中经常被提到的两个概念,很多人会将它们混淆。在金融领域中,了解这两个概念的差异十分重要。本文将从多个视点深入分析总市值和总财物的差异。 1.定义 总市值是指公司发行的一切股票的商场总价值。所谓商场总价值…...
CTF安全竞赛介绍
目录 一、赛事简介 二、CTF方向简介 1.Web(Web安全) (1)简介 (2)涉及主要知识 2.MISC(安全杂项) (1)介绍 (2)涉及主要知识 3…...
DC/DC开关电源学习笔记(四)开关电源电路主要器件及技术动态
(四)开关电源电路主要器件及技术动态 1.半导体器件2.变压器3.电容器4.功率二极管5.其他常用元件5.1 电阻5.2 电容5.3 电感5.4 变压器5.5 二极管5.6 整流桥5.7 稳压管5.8 绝缘栅-双极性晶体管1.半导体器件 功率半导体器件仍然是电力电子技术发展的龙头, 电力电子技术的进步必…...
数据可视化与数字孪生:理解两者的区别
在数字化时代,数据技术正在引领创新,其中数据可视化和数字孪生是两个备受关注的概念。尽管它们都涉及数据的应用,但在本质和应用方面存在显著区别。本文带大探讨数据可视化与数字孪生的差异。 概念 数据可视化: 数据可视化是将复…...
C++ socket编程(TCP)
服务端保持监听客户端, 服务端采用select实现,可以监听多个客户端 客户端源码 在这里插入代码片 #include <iostream> //#include <windows.h> #include <WinSock2.h> #include <WS2tcpip.h> using namespace std; #pragma co…...
ldd用于打印程序或库文件所依赖的共享库列表
这是一个Linux命令行指令,将两个常用的命令 ldd 和 grep 组合使用。我来逐一为您解释: ldd: 这是一个Linux工具,用于打印程序或库文件所依赖的共享库列表。通常,当你有一个可执行文件并且想知道它链接到哪些动态库时,你…...
vue+elementUI el-table实现单选
if (selection.length > 1) {this.$refs.table.clearSelection();this.$refs.table.toggleRowSelection(selection.pop());}...
前端组件库造轮子——Message组件开发教程
前端组件库造轮子——Message组件开发教程 前言 本系列旨在记录前端组件库开发经验,我们的组件库项目目前已在Github开源,下面是项目的部分组件。文章会详细介绍一些造组件库轮子的技巧并且最后会给出完整的演示demo。 文章旨在总结经验,开…...
单片机第二季:温度传感器DS18B20
目录 1,DS18B20介绍 2,DS18B20数据手册 2.1,初始化时序 2.2,读写时序 3,DS18B20工作流程 4,代码 1,DS18B20介绍 DS18B20的基本特征: (1)内置集成ADC,外部数字接…...
抓包工具fiddler的基础知识
目录 简介 1、作用 2、使用场景 3、http报文分析 3.1、请求报文 3.2、响应报文 4、介绍fiddler界面功能 4.1、AutoResponder(自动响应器) 4.2、Composer(设计请求) 4.3、断点 4.4、弱网测试 5、app抓包 简介 fiddler是位于客户端和服务端之间的http代理 1、作用 监控浏…...
监控基本概念
监控:这个词在不同的上下文中有不同的含义,在讲到监控MySQL或者监控Redis时,这里只涉及数据采集和可视化,不涉及告警引擎和事件处理。要是监控系统的话,不但包括数据采集和可视化,而且也包括告警和事件发送…...
【数据结构】 七大排序详解(壹)——直接插入排序、希尔排序、选择排序、堆排序
文章目录 🍀排序的概念及引用🐱👤排序的概念🐱👓排序运用🐱🐉常见的排序算法 🌴插入排序🎋基本思想:🛫直接插入排序📌算法步骤&…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
