Spring 系列(二):BeanDefinition 是如何被加载进容器的
Spring 容器启动的第一件大事,并不是创建 Bean,而是构建容器结构。
而容器结构的核心载体,正是 BeanDefinition。
这一篇作为 Spring 系列的第一篇,我们只做一件事:
系统梳理 Spring 中 BeanDefinition 的所有加载方式,以及它们各自解决什么问题。
一、先统一一个认知:Spring 到底在"加载"什么?
在讨论"加载 Bean 的方式"之前,必须先纠正一个常见但模糊的说法。
Spring 启动早期,并不会加载 Bean 实例,而是加载:
text
BeanDefinition
它描述的是:
- 这个 Bean 是什么类型
- 该如何创建
- 作用域是什么
- 依赖关系如何
所有加载方式的终点,都是向容器中注册 BeanDefinition。
只要你抓住这一点,后面所有方式都只是"入口不同,本质一致"。
二、最原始的方式:基于 XML 的 BeanDefinition 加载
这是 Spring 最早期、也是最直观的一种方式。
xml
<bean id="userService" class="com.example.UserService" />
1. 本质做了什么?
- 解析 XML
- 为每个
<bean>标签构建一个BeanDefinition - 注册到
BeanDefinitionRegistry
2. 对应的核心组件
XmlBeanDefinitionReaderDefaultListableBeanFactory
3. 这种方式的意义
- 奠定了 "配置 → BeanDefinition → 容器" 的模型
- 也是后续所有加载方式的设计基础
三、基于配置类的方式:@Configuration + @Bean
随着 Java Config 的出现,Spring 提供了更类型安全的配置方式。
java
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
1. BeanDefinition 是什么时候生成的?
并不是在方法执行时,而是在解析配置类阶段:
@Configuration会被识别为配置类@Bean方法会被解析为BeanDefinition- 实例化逻辑被包装为
FactoryMethod
2. 核心处理器
ConfigurationClassPostProcessor
它本质上是一个 BeanDefinitionRegistryPostProcessor。
四、基于类路径扫描:@Component 体系
这是目前使用最广泛的一种方式。
java
@Component
@Service
@Repository
@Controller
1. 工作流程
text
扫描 classpath
↓
读取 class 元信息(不加载类)
↓
匹配注解条件
↓
构建 BeanDefinition
↓
注册到容器
2. 关键点
- 扫描阶段不会触发类加载
- 只依赖 ASM 读取字节码元信息
3. 核心组件
ClassPathBeanDefinitionScanner
五、通过 @Import 扩展 BeanDefinition 来源
@Import 是 Spring 中极其重要、但经常被低估的一个入口。
java
@Import({A.class, B.class})
public class AppConfig {}
1. @Import 的三种形态
1️⃣ 直接导入普通类
- 被当作普通 Bean 注册
2️⃣ ImportSelector
java
public class MySelector implements ImportSelector {
public String[] selectImports(...) {
return new String[]{"com.example.A"};
}
}
- 根据条件动态决定注册哪些 BeanDefinition
3️⃣ ImportBeanDefinitionRegistrar
- 直接操作
BeanDefinitionRegistry - 精确、底层、强控制力
六、通过 FactoryBean 间接定义 Bean
java
public class MyFactoryBean implements FactoryBean<UserService> {
public UserService getObject() {
return new UserService();
}
}
1. 特点
- 容器中注册的是
FactoryBean的 BeanDefinition - 实际暴露的是
getObject()返回的对象
2. 使用场景
- 框架级对象构建
- 复杂初始化逻辑
七、容器级扩展点:BeanDefinitionRegistryPostProcessor
这是最高优先级、最底层的扩展方式。
1. 能做什么?
- 在所有 Bean 实例化之前
- 动态注册、修改、删除 BeanDefinition
2. 典型使用场景
- Spring Boot 自动装配
- Dubbo / MyBatis 等框架接入
八、一张总览图(思维模型)
text
XML / @Bean / @Component / @Import
↓
BeanDefinitionReader / Scanner
↓
BeanDefinitionRegistry
↓
冻结容器结构
入口可以多样,但终点只有一个。
九、为什么 Spring 要设计这么多加载方式?
不是为了复杂,而是为了:
- 不同配置风格
- 不同扩展粒度
- 不同控制级别
但无论哪一种,Spring 都严格遵守一条原则:
一切能力,先落到 BeanDefinition 上。
十、总结
Spring 的启动,并不是"创建 Bean",而是"构建容器结构"。
而 BeanDefinition 的加载方式,决定了:
- Bean 从哪里来
- 是否存在
- 以什么形态存在
在下一篇中,我们将从 BeanDefinition 冻结之后 继续,正式进入:
Bean 是如何被创建出来,并被生命周期精细管理的。
这,才是 IoC 真正开始生效的地方。