第二讲:BeanFactory的实现
BeanFactory的实现
- 1. 环境准备
- 2. 初始化DefaultListableBeanFactory
- 3. 手动注册BeanDefinition
- 4. 手动添加后置处理器
- 5. 获取被依赖注入的Bean对象
- 6. 让所有的单例bean初始化时加载
- 7. 总结
Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,这里出于怀旧的原因,把它们都列出来,供大家参考
- DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转和依赖注入功能,都是它来实现
- ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
- FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
- XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
- AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
- AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
- AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
- AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)
另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来。
1. 环境准备
在开始之前,先准备如下代码:
/*** 测试BeanFactory的实现类** @Date 2023/8/20 15:20*/
@Slf4j
public class FactoryImplApplication {public static void main(String[] args) {// TODO}@Configurationstatic class Config{@Beanpublic Component01 bean1() {return new Component01();}@Beanpublic Component02 bean2() {return new Component02();}}static class Component01 {@Resourceprivate Component02 bean02;public Component01() {System.out.println("Component01构造器~~~");}public Component02 getBean02() {return bean02;}
}static class Component02 {public Component02() {System.out.println("Component02构造器~~~");}}
}
BeanDefinition 描述了这个 bean 的创建蓝图:scope 是什么、用构造还是工厂创建、初始化销毁方法是什么,等等
2. 初始化DefaultListableBeanFactory
// 仅创建BeanFactory,并没有创建ApplicationContext,此时打印容器中的BeanDefinition个数,一个都没有。
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
log.info("仅创建beanFactory时,不会自动创建其他的BeanDefinition,此时BeanDefinition个数为:{}", beanFactory.getBeanDefinitionCount());

3. 手动注册BeanDefinition
手动创建Config.class的BeanDefinition并注册到BeanFactory中。
AbstractBeanDefinition configBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", configBeanDefinition);
log.info("向BeanFactory手动注册一个BeanDefinition,此时BeanDefinition个数为:{}", beanFactory.getBeanDefinitionCount());

4. 手动添加后置处理器
学过Spring的应该都知道,Config类上@Configuration,并且里面的方法上有@Bean,那么这个方法的返回对象应该也会被注入容器中。但这边为什么没有呢?这是因为@Configuration并没有被解析,它是由BeanFactory后置处理器来处理的(ConfigurationClassPostProcessor)。主要功能是补充了一些BeanDefinition。
接着,我们给它添加一些常用的后置处理器并调用postProcessBeanFactory(),重新打印日志。
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
log.info("向BeanFactory添加一些常用的BeanFactory后置处理器后,此时BeanDefinition个数为:{}", beanFactory.getBeanDefinitionCount());
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).forEach((key, value) -> {// 调用BeanFactory后置处理器value.postProcessBeanFactory(beanFactory);
});Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);

此时,BeanFactory中就有我们需要的BeanDefinition了。
注意:这几个后置处理器非常重要!!!下面会用得到!!!
注意:这几个后置处理器非常重要!!!下面会用得到!!!
注意:这几个后置处理器非常重要!!!下面会用得到!!!
BeanFactory后置处理器:补充BeanDefinition
Bean后置处理器:针对Bean的生命周期的各个阶段提供扩展,例如解析@Autowired、@Resource等
5. 获取被依赖注入的Bean对象
有了BeanDefinition之后,就可以获取Bean了。但BeanFactory不会主动创建Bean,调用getBean()的时候才会被创建。
我们试着获取bean01:
Component01 bean01 = beanFactory.getBean(Component01.class);
System.out.println("从容器中获取的bean01:" + bean01);
System.out.println("被依赖注入的bean02:" + bean01.getBean02());
运行结果:

bean01被成功创建,但是bean02好像并没有被依赖注入???
这是因为创建bean01之后,beanFactory并不会主动依赖注入,还需要添加Bean后置处理器进行处理。由于第4步注册过BeanDefinition了(registerAnnotationConfigProcessors),我们现在只需要将它们添加到beanFactory的beanPostProcessors中就行了。
- internalAutowiredAnnotationProcessor:@Autowired
- internalCommonAnnotationProcessor:@Resource
因此,在geanBean()之前,执行添加如下代码:
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
重新运行:

bean02被成功注入了。
6. 让所有的单例bean初始化时加载
目前所有的单例bean都是懒加载的,只有在getBean()时才会创建。但是实际上应该在应用启动的时候就把大部分的bean加载,而不是使用到时才加载的。
只需开启beanFactory的初始化加载就行了。
// 初始化所有的单例Bean
beanFactory.preInstantiateSingletons();System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
Component01 bean01 = beanFactory.getBean(Component01.class);
System.out.println("从容器中获取的bean01:" + bean01);
System.out.println("被依赖注入的bean02:" + bean01.getBean02());
如何去验证呢?我们在getBean()之前,先输出了一行分隔符。可以看到构造方法在分割符之前就被调用了。

7. 总结
BeanFactory不会做:
- 不会主动调用BeanFactory后置处理器(对应本文第4点)
- 不会主动添加Bean后置处理器(对应本文第4、5点)
- 不会主动初始化单例(对应本文第6点)
- 不会解析beanFactory,不会解析${}和#{}
相关文章:
第二讲:BeanFactory的实现
BeanFactory的实现 1. 环境准备2. 初始化DefaultListableBeanFactory3. 手动注册BeanDefinition4. 手动添加后置处理器5. 获取被依赖注入的Bean对象6. 让所有的单例bean初始化时加载7. 总结 Spring 的发展历史较为悠久,因此很多资料还在讲解它较旧的实现,…...
vue2+Spring Boot2.7 大文件分片上传
之前我们文章 手把手带大家实现 vue2Spring Boot2.7 文件上传功能 将了上传文件 但如果文件很大 就不太好处理了 按正常情况甚至因为超量而报错 这里 我弄了个足够大的文件 我们先搭建 Spring Boot2.7 环境 首先 application.yml 代码编写如下 server:port: 80 upload:path:…...
Vite更新依赖缓存失败,强制更新依赖缓存
使用vitets开发一段时间了,感觉并不是想象中的好用,特别是出现些稀奇古怪的问题不好解决,比如下面这个问题 上午9:50:08 [vite] error while updating dependencies: Error: ENOENT: no such file or directory, open E:/workspace-dir/node…...
Linux命令200例:tail用来显示文件的末尾内容(常用)
🏆作者简介,黑夜开发者,全栈领域新星创作者✌。CSDN专家博主,阿里云社区专家博主,2023年6月csdn上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 &…...
【Unity每日一记】进行发射,位置相关的方法总结
👨💻个人主页:元宇宙-秩沅 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 秩沅 原创 👨💻 收录于专栏:uni…...
MISRA 2012学习笔记(3)-Rules 8.4-8.7
文章目录 Rules8.4 字符集和词汇约定(Character sets and lexical conventions)Rule 4.1 八进制和十六进制转译序列应有明确的终止识别标识Rule 4.2 禁止使用三字母词(trigraphs) 8.5 标识符(Identifiers)Rule 5.1 外部标识符不得重名Rule 5.2 同范围和命名空间内的标识符不得重…...
centos7组件搭建
Linux(包括centos) 如何查看服务器内存、CPU su - root 切换用户 centos 密码 空格 https://blog.csdn.net/weixin_45277161/article/details/131524555 CentOS 7 安装 Docker 的详细步骤 https://blog.csdn.net/qq_39997939/article/details/13100…...
webpack5和webpack4的一些区别
自动清除打包目录 webpack4 // bash npm i clean-webpack-plugin -D //webpack.config.js const {CleanWebpackPlugin} require(clean-webpack-plugin); module.exports {plugins: [new CleanWebpackPlugin()} } webpack5 module.exports {output: {clean: true} } topLevel…...
攻防世界-fileclude
原题 解题思路 直接展示源码了,flag.php应该存放了flag,在file1与file2都不为空且file2是“hello ctf”时file1将被导入。接下来做法很明显,让file为flag.php,file2为“hello ctf”。“?file1php://filter/readconvert.base64-en…...
深度学习的“前世今生”
1、“感知机”的诞生 20世纪50年代,人工智能派生出了这样两个学派,分别是“符号学派”及“连接学派”。前者的领军学者有Marvin Minsky及John McCarthy,后者则是由Frank Rosenblatt所领导。 “符号学派”的人相信对机器从头编程,…...
第一百一十九回 如何通过蓝牙设备读写数据
文章目录 概念介绍实现方法示例代码经验总结我们在上一章回中介绍了如何获取蓝牙状态相关的内容,本章回中将介绍 如何通过蓝牙设备读写数据。闲话休提,让我们一起Talk Flutter吧。 概念介绍 通过蓝牙设备读写数据有两种方法: 一种是读写Characteristics;一种是读写Descri…...
linux:Temporary failure in name resolutionCouldn’t resolve host
所有域名无法正常解析。 ping www.baidu.com 等域名提示 Temporary failure in name resolution错误。 rootlocalhost:~# ping www.baidu.com ping: www.baidu.com: Temporary failure in name resolution rootlocalhost:~# 一、ubuntu/debian(emporary failure i…...
C 语言的 sprintf() 函数
<stdio.h> 原型: int sprintf(char *str, const char *format, …) 发送格式化输出到 str 所指向的字符串。 参数 str – 这是指向一个字符数组的指针,该数组存储了 C 字符串。 format – 这是字符串,包含了要被写入到字符串 str 的文本。它…...
李沐pytorch学习-卷积网络及其实现
一、卷积概述 1.1 基本定义 卷积计算过程如图1所示,即输入矩阵和核函数(filter)对应的位置相乘,然后相加得到输出对应位置的数。 图1. 卷积计算过程 该过程可以形象地从图2中展现。 图2. 二维卷积示意图 1.2 实现互相关运算的代…...
记录:win10物理机ping不通虚拟机上的docker子网(已解决)
【说明】 windows10:已关闭防火墙 linux发行版本:centos7.9(已禁用SElinux、已关闭防火墙) 虚拟机软件:VMware Workstation 17 虚拟机网络模式:NAT模式 docker版本:20.4.5 docker网络模式…...
深入浅出Pytorch函数——torch.nn.init.kaiming_normal_
分类目录:《深入浅出Pytorch函数》总目录 相关文章: 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...
D. Anton and School - 2
范德蒙德恒等式 考虑统计每一个右括号位置的贡献,也就是每个右括号作为右边起点的贡献 其中i0的时候,r-1<r-0,故i0时贡献为0,直接套用恒等式不会有影响 #include <bits/stdc.h> using namespace std; typedef long long int ll; # d…...
xcode把包打到高版本的iPhone里
打开xcode CTRLb build工程,build成功 把手机连到mac,在xcode选项卡里面的window里面选中device and simulator 打开对应的手机的页面 然后在工程目录下build成功过后有一个product的文件夹里面,直接把app拖到对应的手机的窗口就可以不用…...
PMP项目管理考试小结
一、初步了解 每年有多次考试的机会,大概每三-四个月有一次考试机会,我下面分享的是我考试: 考试时间:8月19日 上午9:00-12:50 考试地点:北京市丰台区首都经济贸易大学(城市不一样考点不一样) …...
【NAS群晖drive异地访问】使用cpolar远程访问内网Synology Drive「内网穿透」
文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…...
Youtu-Parsing解析古籍与历史档案:助力文化遗产数字化与检索
Youtu-Parsing解析古籍与历史档案:助力文化遗产数字化与检索 你有没有想过,那些躺在博物馆或图书馆深处、纸张泛黄、字迹模糊的古籍和历史档案,如何才能被更多人方便地查阅和研究?过去,这需要研究者花费大量时间&…...
CogVideoX-2b效果实测:中文vs英文提示词生成质量差异分析
CogVideoX-2b效果实测:中文vs英文提示词生成质量差异分析 1. 引言:当AI导演遇到不同语言 想象一下,你有一个能听懂你说话、并把你描述的场景变成视频的AI导演。你告诉它:“一个宇航员在月球上漫步,远处是蓝色的地球。…...
基于图像的深度学习与MVS三维重建全流程服务 支持远程部署定制 含pcl/c++/matlab...
基于图像的深度学习MVS三维重建全流程 可远程部署,可定制 点云pcl,c,matlab开发,基于图像三维重建,点云算法开发 只需要提供摄的图像,即可生成完整的三维模型(大小场景均可)上周去爬了个浙西的小众山&#…...
SJA1105Q升级踩坑记:RGMII V2.0时序下,33Ω串阻为何成了千兆通信的‘隐形杀手’?
SJA1105Q升级中的RGMII V2.0时序陷阱:33Ω串阻如何摧毁千兆通信稳定性 当NXP SJA1105Q这款号称"增强版"的工业交换机芯片落到我们硬件工程师手中时,谁曾想PCB上那些看似无害的33Ω小电阻,竟会成为千兆通信系统的阿喀琉斯之踵。这不…...
FedProto:跨异构客户端的原型联邦学习实践指南
1. 从零理解FedProto的核心思想 第一次听说FedProto时,我正被一个医疗影像分析项目搞得焦头烂额。五家医院的数据就像五个方言区——同样的病症在CT影像上呈现的特征分布天差地别。传统联邦学习就像让这些医院用各自的方言写报告,再强行翻译成标准语&…...
HunyuanVideo-Foley私有部署全攻略:RTX4090D专用优化,轻松搭建AI视频生成环境
HunyuanVideo-Foley私有部署全攻略:RTX4090D专用优化,轻松搭建AI视频生成环境 在AI视频生成领域,最令人沮丧的莫过于看着别人的演示视频效果惊艳,而自己却卡在环境配置和模型部署的泥潭中。从CUDA版本冲突到显存不足崩溃…...
Ollama平台部署GLM-4.7-Flash:从零开始搭建本地大模型服务
Ollama平台部署GLM-4.7-Flash:从零开始搭建本地大模型服务 1. 为什么选择GLM-4.7-Flash? 在众多开源大模型中,GLM-4.7-Flash以其独特的定位脱颖而出。这个30B参数的MoE(混合专家)模型,在性能与效率之间取…...
零基础玩转OpenClaw:Qwen3-32B-Chat镜像云端体验指南
零基础玩转OpenClaw:Qwen3-32B-Chat镜像云端体验指南 1. 为什么选择云端体验OpenClaw? 第一次听说OpenClaw时,我正被各种本地部署的依赖项折磨得焦头烂额。作为一个习惯在MacBook上写代码的开发者,光是配置CUDA环境就让我望而却…...
低功耗电源开关电路设计与MCU控制实现
1. 经典电源开关电路设计与分析1.1 系统架构概述该电源开关电路采用三级晶体管控制架构,实现以下核心功能:低功耗待机模式(静态电流<10μA)按键触发启动机制MCU控制的自锁功能软件可控的电源关断系统工作电压为9V输入ÿ…...
WWW-万维网
万维网的概念与组成结构万维网(World Wide Web,WWW)是一个分布式的信息存储空间,在这个空间中:一个事物被称为一样 “资源”,并由一个全域 “统一资源定位符”(URL)标识。这些资源通…...
