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

相关推荐
泡海椒9 小时前
评分系统最佳实践:JQuick-Java实现权重、阈值动态配置评分
java·人工智能·python
三克的油9 小时前
java-学习1
java·开发语言·学习
mldong9 小时前
弃用 ZCode,转 DeepSeek Harness:我用 GitHub Actions 自建 Windows 打包的全实录
java·架构
acd1200910 小时前
Java 线程池线上高频踩坑记录
spring
Wang's Blog10 小时前
Java 接入Redis: Redis下载与源码编译安装
java·服务器·redis
农民工老王10 小时前
故障排查:反向代理转发 GET 请求时 JDK 自动追加 Content-Length:0 致后端 500
java·httpclient·content-length
泡海椒10 小时前
jquick-pdf 防止 PDF 内容分页断裂:keepTogether 属性妙用
java·开发语言·pdf
知识的搬运工旺仔10 小时前
CREATE INDEX CONCURRENTLY:线上建索引不阻塞 DML 的代价与坑
数据库·后端·sql
vx_BS8133010 小时前
【项目编号:project51381】Spring Boot 婚纱摄影管理系统:套餐预约、在线咨询、支付与客片展示的一站式业务实现
java·spring boot·eclipse·tomcat·mybatis
IT毕设实战小研10 小时前
基于大数据的商场商铺数据分析与可视化的设计与实现
android·java·大数据·django·课程设计