Spring如何把bean注册到容器里

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() 完成时,才会真正创建实例。

相关推荐
silvamux_11 小时前
10款模型写种草笔记:可发布性、返工来源与成本数据缺口复盘
java·数据库·笔记
步行cgn12 小时前
StringHttpMessageConverter 详解:Spring MVC 中字符串读写的默认转换器
java·spring·mvc
摇滚侠12 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
小蒜学长12 小时前
基于Spring Boot+Vue3的在线医疗咨询系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·在线问诊·在线医疗咨询系统
Deng桑12 小时前
SpringAI写第一个Agent-把工单循环跑起来
java·agent·工具调用·spring ai
陈随易13 小时前
下载神器!Motrix 停更三年后重写回归
前端·后端·程序员
每天一道题13 小时前
CAP、Raft 与多数派:分布式系统如何达成一致?
分布式·后端·微服务
前端的日常13 小时前
一个人+AI做情侣食谱小程序,0 费用的服务器
前端·后端
IT_陈寒14 小时前
Vue的v-for里用了个临时变量,页面直接挂了
前端·人工智能·后端
步行cgn14 小时前
@ResponseBody 详解:Spring MVC 中把返回值直接写入响应体
java·spring·mvc