手写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时,这里只涉及数据采集和可视化,不涉及告警引擎和事件处理。要是监控系统的话,不但包括数据采集和可视化,而且也包括告警和事件发送…...
【数据结构】 七大排序详解(壹)——直接插入排序、希尔排序、选择排序、堆排序
文章目录 🍀排序的概念及引用🐱👤排序的概念🐱👓排序运用🐱🐉常见的排序算法 🌴插入排序🎋基本思想:🛫直接插入排序📌算法步骤&…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
