1.把bean都注册到spring容器的流程是什么
好问题!你问的是 Bean注册流程,这和加载流程是"前与后"的关系。
简单区分:
- 注册 :把Bean的定义信息(
BeanDefinition)放进容器 - 加载:根据定义信息创建出对象实例
注册的几种入口方式
| 方式 | 注册入口 | 典型场景 |
|---|---|---|
| XML配置 | XmlBeanDefinitionReader |
老项目、Spring 2.x |
| 注解配置类 | AnnotatedBeanDefinitionReader |
@Configuration + @Bean |
| 组件扫描 | ClassPathBeanDefinitionScanner |
@Component、@Service等 |
| 编程式注册 | GenericBeanDefinition + registerBeanDefinition |
Spring Boot自动配置、自定义starter |
| Import导入 | @Import |
手动导入配置类或ImportSelector |
核心流程图(以注解扫描为例)
启动Spring容器
↓
new AnnotationConfigApplicationContext(Config.class)
↓
【阶段1】读取配置类 → 注册配置类自身
↓
【阶段2】处理@Bean方法 → 每个@Bean生成一个BeanDefinition
↓
【阶段3】执行@ComponentScan → 扫描包下所有带注解的类
↓
【阶段4】处理@Import、@ImportResource等
↓
【阶段5】所有BeanDefinition注册到BeanDefinitionRegistry
↓
【阶段6】调用refresh() → 开始Bean加载(实例化、初始化...)
详细步骤拆解(源码视角)
1. 容器启动
java
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
构造函数内部:
java
// 1. 创建 DefaultListableBeanFactory(核心注册中心)
// 2. 创建 AnnotatedBeanDefinitionReader(处理@Bean等)
// 3. 创建 ClassPathBeanDefinitionScanner(处理@ComponentScan)
// 4. 调用 refresh()
2. 注册配置类本身
AnnotatedBeanDefinitionReader 把 AppConfig.class 转成 AnnotatedGenericBeanDefinition
- beanName = "appConfig"
- beanClass = AppConfig.class
- 标记为
@Configuration类
3. 处理 @ComponentScan
ClassPathBeanDefinitionScanner 扫描指定包:
- 找到所有带
@Component、@Service、@Repository、@Controller的类 - 每个类生成
ScannedGenericBeanDefinition - 解析
@Scope、@Lazy、@Primary等元数据
4. 处理 @Bean 方法
ConfigurationClassPostProcessor(BeanFactoryPostProcessor):
- 解析
@Configuration类中的@Bean方法 - 每个
@Bean方法生成ConfigurationClassBeanDefinition - beanName = 方法名(默认)或
@Bean("name")指定的
5. 处理 @Import
三种情况:
| @Import的值 | 处理方式 |
|---|---|
| 普通类 | 直接注册 |
ImportSelector |
调用 selectImports() 返回类名数组,逐个注册 |
ImportBeanDefinitionRegistrar |
调用 registerBeanDefinitions(),编程式注册 |
6. 注册到 BeanDefinitionRegistry
所有 BeanDefinition 最终都存入 DefaultListableBeanFactory 的:
java
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
7. 等待 refresh() 触发加载
注册完成后,refresh() 中的 finishBeanFactoryInitialization() 才会触发 getBean(),真正开始加载流程。
关键对象与作用
| 角色 | 实现类 | 职责 |
|---|---|---|
| 注册中心 | BeanDefinitionRegistry |
定义注册/获取BeanDefinition的接口 |
| 核心实现 | DefaultListableBeanFactory |
真正的注册表,持有beanDefinitionMap |
| 定义载体 | BeanDefinition |
描述Bean的元信息(类名、作用域、依赖、构造器等) |
| XML读取器 | XmlBeanDefinitionReader |
解析XML → BeanDefinition |
| 注解读取器 | AnnotatedBeanDefinitionReader |
处理配置类和@Bean |
| 注解扫描器 | ClassPathBeanDefinitionScanner |
扫描@Component注解 |
一句话总结面试回答
Bean注册流程本质是将配置(XML/注解/Java Config)解析成
BeanDefinition对象,然后存入DefaultListableBeanFactory的beanDefinitionMap中。入口是BeanDefinitionReader或ClassPathBeanDefinitionScanner,核心是BeanDefinitionRegistry.registerBeanDefinition()。注册完成后,由refresh()触发的finishBeanFactoryInitialization()开始真正的实例化加载流程。
补充:注册 vs 加载 时间线
【注册阶段】 【加载阶段】
解析配置 → 生成BeanDefinition 实例化对象 → 属性填充 → 初始化
↓ ↓
存入Map 调用getBean()创建实例
↓ ↓
BeanFactoryPostProcessor BeanPostProcessor
可修改BeanDefinition 可修改/代理Bean实例
注册阶段不创建对象,只是"登记造册"。只有第一次调用 getBean() 或容器 refresh() 完成时,才会真正创建实例。