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. 注册配置类本身

AnnotatedBeanDefinitionReaderAppConfig.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 对象,然后存入 DefaultListableBeanFactorybeanDefinitionMap 中。入口是 BeanDefinitionReaderClassPathBeanDefinitionScanner,核心是 BeanDefinitionRegistry.registerBeanDefinition()。注册完成后,由 refresh() 触发的 finishBeanFactoryInitialization() 开始真正的实例化加载流程。

补充:注册 vs 加载 时间线

复制代码
【注册阶段】                      【加载阶段】
                                 
解析配置 → 生成BeanDefinition      实例化对象 → 属性填充 → 初始化
         ↓                                    ↓
存入Map                            调用getBean()创建实例
         ↓                                    ↓
BeanFactoryPostProcessor           BeanPostProcessor
可修改BeanDefinition               可修改/代理Bean实例

注册阶段不创建对象,只是"登记造册"。只有第一次调用 getBean() 或容器 refresh() 完成时,才会真正创建实例。

相关推荐
恼书:-(空寄2 小时前
静态代理与动态代理,Spring AOP底层精髓全解析
spring·动态代理·静态代理
阿丰资源2 小时前
基于SpringBoot+MySQL的网上订餐系统(附源码)
spring boot·后端·mysql
代码不加糖2 小时前
0基础搭建前后端分离项目:实现菜单与界面左右布局
java·前端·javascript·mysql·elementui·mybatis
希望永不加班2 小时前
SpringBoot 敏感数据脱敏(序列化层)
java·spring boot·后端·spring
希望永不加班2 小时前
SpringBoot 数据库索引优化:慢查询分析
java·数据库·spring boot·后端·spring
胡利光2 小时前
Harness Engineering 02|Repo Harness:让仓库对 Agent 可读
java·junit·单元测试
彩票管理中心秘书长2 小时前
MySQL数据库新建流程和字符集详细介绍
后端
geovindu2 小时前
go: Proxy Pattern
开发语言·后端·设计模式·golang·代理模式
彩票管理中心秘书长2 小时前
MySQL 用户与权限管理 (DCL) 操作命令大全
后端