Spring IoC内核解析:基于DefaultListableBeanFactory构建最小依赖容器
本文旨在探讨Spring框架控制反转(IoC)功能的最小核心实现。我们将通过剥离ApplicationContext的所有增强特性,聚焦于最本质的“注册Bean定义 → 创建Bean实例 → 管理依赖关系”这条路径,揭示Spring容器的骨架。
核心定义:仅保留让容器能完成上述三大核心功能的最小代码路径,剔除事件、国际化、环境抽象等所有非必要模块,直击IoC本质。
最小可行方案:三步核心流程
以下代码展示了构建一个最小Spring容器的完整过程:
// 1. 创建核心容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2. 注册Bean定义(使用编程式API)
// 注册Mapper Bean(无依赖)
RootBeanDefinition mapperDef = new RootBeanDefinition(UserMapper.class);
beanFactory.registerBeanDefinition("userMapper", mapperDef);
// 注册Service Bean(带构造函数注入)
RootBeanDefinition serviceDef = new RootBeanDefinition(UserService.class);
// 使用构造函数注入(推荐方式)
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addIndexedArgumentValue(0, new RuntimeBeanReference("userMapper"));
serviceDef.setConstructorArgumentValues(args);
beanFactory.registerBeanDefinition("userService", serviceDef);
// 注意:此处特意不添加任何BeanPostProcessor(如AutowiredAnnotationBeanPostProcessor)
// 3. 初始化单例Bean(自动处理依赖)
beanFactory.preInstantiateSingletons();
// 验证获取Bean
UserService userService = beanFactory.getBean("userService", UserService.class);
System.out.println("容器启动成功!Service: " + userService);
这套方案的核心价值在于:仅保留IoC三大能力,即Bean定义注册、依赖关系管理和生命周期控制。
核心流程深度拆解与内部工作
上述三步流程的每一步都包含了复杂的内部操作和潜在的失败场景,具体拆解如下:
步骤一:创建DefaultListableBeanFactory
- 核心操作:
new DefaultListableBeanFactory()
- 内部工作拆解:
- 初始化核心存储结构,包括
beanDefinitionMap(BeanDefinition注册表)、singletonObjects(单例对象缓存)和singletonFactories(三级缓存首层)。
- 设置默认的
ConversionService。
- 初始化
PropertyEditorRegistry。
- 失败场景:
- 内存溢出(OutOfMemoryError):极端场景下初始化大型Map结构失败。
- 类加载失败:
DefaultListableBeanFactory自身加载异常,属于JVM级错误。
步骤二:注册BeanDefinition
- 核心操作:
registerBeanDefinition(...)
- 内部工作拆解:
- 验证
BeanDefinition有效性(如检查scope、class属性)。
- 检查是否重复注册(根据
allowBeanDefinitionOverriding策略决定是否允许覆盖)。
- 此时容器配置尚未冻结(
configurationFrozen=false),允许修改。
- 关键:通过
PropertyValues或ConstructorArgumentValues显式声明依赖关系,这是替代@Autowired注解的核心手段。
- 失败场景:
- 定义无效:例如scope被设置为
“invalid”,会抛出BeanDefinitionValidationException。
- 重复注册冲突:同名Bean已存在且
allowBeanDefinitionOverriding=false时,抛出BeanDefinitionStoreException。
- 依赖声明错误:
RuntimeBeanReference指向了一个不存在的Bean,此错误通常延迟到第三步创建Bean时才暴露。
步骤三:预实例化单例Bean
- 核心操作:
preInstantiateSingletons()
- 内部工作拆解:
- 遍历所有非延迟加载的单例
BeanDefinition。
- 针对每个Bean,执行单Bean创建链:
getSingleton(beanName):检查三级缓存,解决循环依赖(本最小方案中主要依赖一级缓存)。
createBean(beanName, mbd, args):核心创建过程。
- 实例化:通过反射调用构造器创建对象。
- populateBean():根据
BeanDefinition中的PropertyValues进行依赖注入(非@Autowired方式)。
- initializeBean:若Bean实现了
BeanNameAware等接口,则调用回调方法。本最小方案中,复杂的注解处理(如@PostConstruct)被省略。
- 调用
InitializingBean.afterPropertiesSet()方法。
- 将创建完成的Bean注册到
singletonObjects缓存中。
- 失败场景:
- 类不存在:
ClassNotFoundException。
- 构造失败:构造器抛出异常,导致
BeanCreationException。
- 依赖缺失:
PropertyValues中引用的Bean尚未注册,抛出NoSuchBeanDefinitionException。
- 循环依赖:在构造器注入场景下遇到循环依赖,抛出
BeanCurrentlyInCreationException。
- 初始化失败:
afterPropertiesSet()方法执行抛出异常,导致BeanCreationException。
最小容器启动流程图解
整个最小容器的启动流程可以清晰地概括为以下几个步骤:
- 开始。
- 创建DefaultListableBeanFactory:初始化
beanDefinitionMap、singletonObjects及三级缓存结构。
- 注册BeanDefinition:验证Bean定义,通过
PropertyValues.add(...)等方式显式声明依赖关系,并注册至BeanDefinitionMap。
- 预实例化单例Bean(核心执行链):
- 遍历单例BeanDefinition。
- 对每个Bean执行
createBean(),包括实例化 → populateBean(依赖注入) → initializeBean(基础生命周期回调)。
- 将成功创建的Bean注册至
singletonObjects。
- 此步骤可能因依赖缺失、循环依赖或初始化异常而失败。
- 容器就绪,Bean可被获取。
该流程省略了ApplicationContext.refresh()中的诸多非必要步骤,例如:
prepareRefresh()(状态管理)
invokeBeanFactoryPostProcessors()(注解扫描、占位符解析)
registerBeanPostProcessors()(支持@Autowired、AOP等)
- 事件、国际化、Web集成等所有扩展功能。
能力对比:最小容器 vs. 完整ApplicationContext
下表对比了最小容器方案与标准ApplicationContext.refresh()流程所提供的核心能力,并判断其必要性:
| 能力 |
最小容器方案 |
ApplicationContext.refresh() |
是否必要 |
| Bean定义注册 |
✅ 编程式注册 |
✅ XML/注解扫描 + 注册 |
必需 |
| 依赖注入 |
✅ 通过BeanDefinition显式声明 |
✅ + @Autowired注解注入 |
必需(实现方式可简化) |
| 单例管理 |
✅ singletonObjects缓存 |
✅ 完整三级缓存 |
必需 |
| 生命周期回调 |
✅ InitializingBean/init-method |
✅ + Aware接口/@PostConstruct |
部分必需(可保留init-method) |
| 注解处理 |
✘ 无 |
✅ ConfigurationClassPostProcessor等 |
非必需 |
| 环境抽象 |
✘ 无 |
✅ Environment/PropertySource |
非必需 |
| 事件机制 |
✘ 无 |
✅ ApplicationEventMulticaster |
非必需 |
| 国际化 |
✘ 无 |
✅ MessageSource |
非必需 |
| Web扩展 |
✘ 无 |
✅ ServletContext集成 |
非必需 |
| 启动状态管理 |
✘ 无 |
✅ active标志/启动时间戳 |
非必需(业务层可自行管理) |
关键设计取舍:
- 依赖显式声明:使用
PropertyValues.add("field", new RuntimeBeanReference("beanName"))替代@Autowired,彻底移除对注解处理器的依赖。
- 无后处理器:不注册任何
BeanPostProcessor,避免了AOP、注解解析等带来的开销。
- 无环境抽象:直接使用类字面量,无需
Environment或PropertySource支持。
- 无事件机制:省略所有事件发布与监听逻辑。
最小容器的价值与适用场景
这种极致简约的方案在多方面体现出其独特价值:
| 维度 |
最小容器方案表现 |
核心价值 |
| 代码量 |
3行核心代码 + 显式依赖声明 |
零配置、零注解、零外部依赖,极致简洁 |
| 内存开销 |
仅Bean实例 + 基础Map结构 |
相比完整ApplicationContext可节省60%以上内存 |
| 启动速度 |
纳秒级(无类路径扫描、无后处理器回调) |
适用于对启动性能极度敏感的嵌入式或实时场景 |
| 可控性 |
100%手动控制Bean生命周期 |
无框架“魔法”,问题定位极其简单直接 |
| 适用场景 |
• 嵌入式设备/资源受限环境<br>• 单元测试中的Mock容器<br>• 自定义框架或容器的基座<br>• 深入学习IoC原理 |
聚焦核心,剥离框架糖衣,直击本质 |
一句话精髓:
Spring容器的本质 = BeanFactory + BeanDefinition注册 + preInstantiateSingletons()。
其余皆为增强。理解此三要素,即掌握IoC内核;剥离所有非必要依赖,方见容器本真。当你需要“最简启动”时,请直接使用DefaultListableBeanFactory——它才是Java Spring IoC的纯粹心脏,完美诠释了后端 & 架构设计中“单一职责”与“最小可用”的原则。
参考资料
[1] 低配版Spring容器:剥离所有非必要依赖的IoC骨架, 微信公众号:https://mp.weixin.qq.com/s/l_4rYiie4DcDaxLiTqL_mQ
版权声明:本文由 云栈社区 整理发布,版权归原作者所有。
|