Spring Boot 加载 BeanDefinitions
的实现原理是 Spring 容器初始化过程中的核心部分。它涉及到 资源定位、解析配置文件、注册 BeanDefinition 等多个步骤,整个流程贯穿了 Spring 容器的启动过程。
一、什么是 BeanDefinition?
在 Spring 容器中,BeanDefinition
是对一个 Bean 的元数据描述,包括:
- Bean 的类名(class)
- 是否是单例(scope)
- 是否懒加载(lazy-init)
- 初始化方法(init-method)
- 销毁方法(destroy-method)
- 依赖关系(depends-on)
- 属性值(property values)
二、Spring Boot 启动时加载 BeanDefinition 的流程
1. SpringApplication.run() 启动入口
SpringApplication.run(Application.class, args);
说明:
- 功能:这是 Spring Boot 应用的启动点,内部会执行一系列操作,其中就包括容器的初始化和 BeanDefinitions 的加载。
- Application.class:标注了 @SpringBootApplication。
2. run()
方法
/**
* 静态工具方法,用于使用默认设置从指定的主类启动一个 {@link SpringApplication}。
*
* 该方法通常在 Java 应用程序的 main 方法中调用,作为 Spring Boot 应用的入口点之一。
*
* @param primarySource 主配置类(通常是标注了 @SpringBootApplication 或 @Configuration 的类)
* @param args 命令行参数(通常由 main 方法传入)
* @return 返回已经运行的 {@link ApplicationContext}(应用上下文)
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
// 将主类包装成数组形式,调用另一个 run 方法
return run(new Class<?>[] { primarySource }, args);
}
说明:
- 功能:此方法用于启动 Spring Boot 应用程序。
- 参数:
primarySource
:应用程序的主配置类,通常包含@SpringBootApplication
注解。args
:命令行参数,传递给应用程序上下文。
- 返回值:返回已经初始化并运行的
ApplicationContext
,可用于获取 Bean 和管理应用生命周期。 - 实现细节:它只是将单个主类封装为数组,并委托给
run(Class<?>[], String[])
方法处理。
3. 继续调用 run()
方法
/**
* 静态工具方法,用于使用默认设置从指定的主配置类数组启动一个 {@link SpringApplication}。
*
* 该方法通常在 Java 应用程序的 main 方法中调用,作为 Spring Boot 应用的标准入口之一。
*
* @param primarySources 一个包含主配置类的数组(通常是标注了 @SpringBootApplication 或 @Configuration 的类)
* @param args 命令行参数(通常由 main 方法传入)
* @return 返回已经运行的 {@link ApplicationContext}(应用上下文)
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 创建一个新的 SpringApplication 实例并运行它
return new SpringApplication(primarySources).run(args);
}
4. 构造函数new SpringApplication(primarySources)
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
/**
* 创建一个新的 {@link SpringApplication} 实例。
*
* 应用上下文将从指定的主配置类(primary sources)中加载 Bean(详见 {@link SpringApplication} 类级文档)。
* 在调用 {@link #run(String...)} 方法之前,可以对创建的实例进行自定义。
*
* @param resourceLoader 要使用的资源加载器,可以为 null
* @param primarySources 主要的 Bean 源(通常是带有 @Configuration 注解的类)
*
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 设置资源加载器
this.resourceLoader = resourceLoader;
// 确保 primarySources 不为 null
Assert.notNull(primarySources, "'primarySources' must not be null");
// 将 primarySources 存储为 LinkedHashSet,保证顺序和唯一性
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据 classpath 推断 Web 应用类型,并设置到 properties 中
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
// 加载 BootstrapRegistryInitializer 的实现类并初始化
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 获取 ApplicationContextInitializer 实例并设置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 获取 ApplicationListener 实例并设置
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断主应用类(即包含 main 方法的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
参数说明:
- resourceLoader:用于加载资源的
ResourceLoader
,如果为null
则使用默认的资源加载机制。 - primarySources:一个或多个主配置类(通常带有
@Configuration
注解),这些类将作为 Spring 容器启动时的主要 Bean 定义来源。
初始化逻辑说明:
- resourceLoader:传入的资源加载器被保存下来,在后续加载 Bean 定义时可能会用到。
- primarySources:主配置类集合被转换为
LinkedHashSet
以保持插入顺序且避免重复,在最后的run()
会用到 - webApplicationType:通过类路径自动推断是否是 Web 应用、响应式 Web 应用还是普通应用。
- bootstrapRegistryInitializers:从
spring.factories
文件中加载所有 BootstrapRegistryInitializer 实现类并初始化。 - initializers:从
spring.factories
中获取所有的ApplicationContextInitializer
并设置到当前实例。 - listeners:从
spring.factories
中获取所有的ApplicationListener
并设置到当前实例。 - mainApplicationClass:通过栈帧分析尝试推断出包含 main 方法的主类。
5. 最后的 run()
方法
/**
* 运行 Spring 应用程序,创建并刷新一个新的 {@link ApplicationContext}(应用上下文)。
*
* 该方法会执行以下主要步骤:
* 1. 创建启动策略(Startup)实例;
* 2. 注册关闭钩子(如果启用);
* 3. 准备引导上下文(BootstrapContext);
* 4. 配置 headless 属性;
* 5. 获取运行监听器并通知 starting 事件;
* 6. 准备环境配置(Environment);
* 7. 打印 Banner;
* 8. 创建应用上下文;
* 9. 准备上下文环境;
* 10. 刷新上下文;
* 11. 调用 ApplicationRunner 和 CommandLineRunner;
* 12. 处理异常和失败情况;
* 13. 最后通知 listeners 应用已就绪。
*
* @param args 应用程序参数(通常由 main 方法传入)
* @return 已经运行的 {@link ConfigurableApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 创建启动策略(用于处理启动指标)
Startup startup = Startup.create();
// 如果启用了注册关闭钩子,则启用它
if (this.properties.isRegisterShutdownHook()) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
// 创建默认的 Bootstrap 上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 初始化可配置的应用上下文为 null
ConfigurableApplicationContext context = null;
// 配置 headless 模式(默认为 true)
configureHeadlessProperty();
// 获取运行监听器并触发 starting 事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 封装应用程序参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境配置(包括系统环境、JVM 参数等)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 打印 Banner(控制台或日志)
Banner printedBanner = printBanner(environment);
// 创建应用上下文(根据 WebApplicationType 决定类型)
context = createApplicationContext();
// 设置应用启动监控
context.setApplicationStartup(this.applicationStartup);
// 准备应用上下文环境(设置 BeanFactoryPostProcessors、ResourceLoader 等)
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文(加载 Bean 定义、初始化单例 Bean)
refreshContext(context);
// 刷新后处理(子类扩展点)
afterRefresh(context, applicationArguments);
// 记录启动耗时
startup.started();
// 如果启用了启动日志,记录启动完成信息
if (this.properties.isLogStartupInfo()) {
new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);
}
// 通知监听器上下文已启动
listeners.started(context, startup.timeTakenToStarted());
// 调用所有 ApplicationRunner 和 CommandLineRunner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 处理运行时异常
throw handleRunFailure(context, ex, listeners);
}
try {
// 如果上下文正在运行,通知监听器应用已准备就绪
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
// 处理就绪阶段的异常
throw handleRunFailure(context, ex, null);
}
// 返回运行中的上下文
return context;
}
方法流程说明:
步骤 | 描述 |
---|---|
1. 启动策略 | 使用 Startup.create() 创建一个启动策略对象,用于统计启动时间等信息。 |
2. 关闭钩子 | 若配置了 registerShutdownHook=true ,则注册 JVM 关闭钩子以优雅关闭上下文。 |
3. 引导上下文 | 创建 DefaultBootstrapContext ,用于支持 CRaC(检查点/恢复)特性。 |
4. Headless 模式 | 设置 java.awt.headless 系统属性,默认为 true,避免图形界面相关问题。 |
5. 监听器通知 starting | 获取 [SpringApplicationRunListener] 并触发 [starting()] 事件。 |
6. 准备环境 | 加载命令行参数、系统环境变量、配置文件等,并绑定到 Environment 对象。 |
7. 打印 Banner | 根据配置决定是否打印 Banner(可以是文本或图片)。 |
8. 创建上下文 | 根据 [WebApplicationType] 创建不同类型的上下文(Servlet/WebFlux/普通应用)。 |
9. 准备上下文 | 设置资源加载器、Bean 名称生成器、添加 BeanFactoryPostProcessor 等。 |
10. 刷新上下文 | 刷新上下文,加载 Bean 定义并初始化单例 Bean。 |
11. 后续处理 | 子类可以重写 [afterRefresh()] 方法进行自定义处理。 |
12. 日志输出 | 输出启动完成的日志信息。 |
13. 触发 Runner | 调用所有 [ApplicationRunner] 和 [CommandLineRunner] 的 [run()] 方法。 |
14. 异常处理 | 捕获异常并通过 ExceptionReporters 报告错误信息。 |
15. 应用就绪 | 通知监听器应用已进入“就绪”状态。 |
6. 准备应用上下文环境 prepareContext()
方法
/**
* 准备应用上下文,设置环境、BeanFactory、初始化器等,并加载 Bean 定义。
*
* 主要步骤包括:
* 1. 设置上下文的 Environment;
* 2. 对上下文进行后处理(子类扩展点);
* 3. 添加 AOT(预编译)生成的初始化器;
* 4. 应用所有的 ApplicationContextInitializer;
* 5. 触发监听器的 contextPrepared 事件;
* 6. 关闭 BootstrapContext;
* 7. 记录启动日志信息;
* 8. 注册 Spring Boot 特有的单例 Bean(如 applicationArguments 和 banner);
* 9. 配置 BeanFactory 的行为(如允许循环引用、覆盖 Bean 定义等);
* 10. 添加懒加载、KeepAlive 等处理器;
* 11. 加载主配置源(如 @Configuration 类);
* 12. 触发监听器的 contextLoaded 事件。
*
* @param bootstrapContext 引导上下文
* @param context 应用上下文
* @param environment 应用环境配置
* @param listeners 运行监听器
* @param applicationArguments 应用程序参数
* @param printedBanner 打印的 Banner 信息
*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置上下文的环境配置
context.setEnvironment(environment);
// 后处理上下文(子类可扩展)
postProcessApplicationContext(context);
// 如果使用了 AOT 模式,添加对应的初始化器
addAotGeneratedInitializerIfNecessary(this.initializers);
// 应用所有已注册的 ApplicationContextInitializer
applyInitializers(context);
// 通知监听器上下文已经准备好
listeners.contextPrepared(context);
// 关闭引导上下文
bootstrapContext.close(context);
// 如果启用了启动日志,则记录相关信息
if (this.properties.isLogStartupInfo()) {
logStartupInfo(context.getParent() == null); // 根上下文信息
logStartupInfo(context); // 子上下文信息
logStartupProfileInfo(context); // 激活的 Profile 信息
}
// 获取上下文的 BeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 注册 springApplicationArguments Bean(供后续使用)
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
// 如果打印了 Banner,则将其注册为单例 Bean
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置 BeanFactory 相关属性(如是否允许循环引用、Bean 覆盖)
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding());
}
}
// 如果启用了懒加载,添加懒加载的 BeanFactoryPostProcessor
if (this.properties.isLazyInitialization()) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 如果启用了 KeepAlive,添加 KeepAlive 监听器以保持 JVM 不退出
if (this.properties.isKeepAlive()) {
context.addApplicationListener(new KeepAlive());
}
// 添加 PropertySourceOrderingBeanFactoryPostProcessor 来调整 PropertySource 的顺序
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// 如果未启用 AOT 模式,加载主配置源(如 @Configuration 类)
if (!AotDetector.useGeneratedArtifacts()) {
Set<Object> sources = getAllSources();
Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined");
load(context, sources.toArray());
}
// 通知监听器上下文已加载完成
listeners.contextLoaded(context);
}
在 Spring Boot 中,AOT(Ahead-Of-Time)模式是一种预编译机制,用于提前生成运行时所需的元数据和配置,从而提升应用的启动性能。要启用 AOT 模式,需要满足特定条件,并通过配置控制是否加载主配置源。
✅ 如何启用 AOT 模式?
1. 确保项目使用了支持 AOT 的 Spring Boot 版本
Spring Boot 从 3.x 开始引入了对 AOT 的支持,建议使用 Spring Boot 3.0+ 和 Java 17+ 环境。
2. 添加必要的依赖
如果你使用的是 Maven 或 Gradle,需确保构建工具能够处理 AOT 阶段的代码生成。
Maven 示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-aot</artifactId>
<scope>provided</scope>
</dependency>
3. 启用 AOT 模式的构建插件
Maven 插件配置:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<aot>true</aot>
</configuration>
</plugin>
Gradle 插件配置:
tasks.named('bootBuildImage') {
aot = true
}
4. 运行 AOT 构建命令
对于 Maven 项目:
mvn clean install -Pnative
或者直接运行:
mvn spring-boot:build-image -Dspring-boot.build-image.aot=true
这会触发 AOT 阶段的代码生成。
🧠 启用 AOT 模式后,为什么可以不加载主配置源?
当启用 AOT 模式时,Spring Boot 会在构建阶段预先分析和生成配置类、Bean 定义等信息,并将它们以静态方式写入到生成的类中。这样在运行时就不再需要动态解析 @Configuration
类或扫描组件,从而跳过传统的 [load()] 方法加载主配置源的过程。
相关代码:
if (!AotDetector.useGeneratedArtifacts()) {
// 如果未启用 AOT,则加载主配置源
Set<Object> sources = getAllSources();
Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined");
load(context, sources.toArray(new Object[0]));
}
这段逻辑位于 [prepareContext(…)] 方法中,它会根据 AotDetector.useGeneratedArtifacts()
的返回值决定是否执行 [load(…)] 加载主配置源。
📌 总结
步骤 | 内容 |
---|---|
✅ 启用方式 | 使用 Spring Boot 3.x+,配置 spring-boot-maven-plugin 或 Gradle 插件,设置 aot=true |
📁 构建结果 | 生成 AOT 预编译类,存放在 META-INF/spring/aot/ 路径下 |
🚫 不加载主配置源 | 在 [prepareContext(…)] 中,通过 AotDetector.useGeneratedArtifacts() 判断是否跳过 [load(…)] |
✅ 验证方式 | 查看日志或生成的 AOT 类 |
💡 补充说明:AOT 与 Native Image 的关系
AOT 是为 Native Image(如 GraalVM)服务的关键优化手段。通过 AOT 预先生成反射、代理、序列化等所需元数据,避免 Native Image 构建时遗漏这些信息,从而减少运行时开销并提高性能。
如果你是面向云原生或 Serverless 场景开发,推荐开启 AOT 并结合 Native Image 使用。
7、假设不启用AOT,prepareContext()
方法会调用load()
方法
if (!AotDetector.useGeneratedArtifacts()) {
// 如果未启用 AOT,则加载主配置源
Set<Object> sources = getAllSources();
Assert.state(!ObjectUtils.isEmpty(sources), "No sources defined");
load(context, sources.toArray(new Object[0]));
}
/**
* 返回一个不可变的集合,包含所有将在调用 {@link #run(String...)} 时添加到 ApplicationContext 的源。
* 此方法将构造函数中指定的主要源(primary sources)与通过 {@link #setSources(Set)} 显式设置的附加源合并。
*
* @return 所有源的不可变集合
*/
public Set<Object> getAllSources() {
// 使用 LinkedHashSet 确保有序性
Set<Object> allSources = new LinkedHashSet<>();
// 添加构造函数中传入的主要源(primarySources)
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
// 添加通过 setSources 设置的额外源(来自 properties)
if (!CollectionUtils.isEmpty(this.properties.getSources())) {
allSources.addAll(this.properties.getSources());
}
// 返回不可变集合,防止外部修改
return Collections.unmodifiableSet(allSources);
}
this.primarySources
在构造函数new SpringApplication(primarySources)
已经传入,简单来说主配置类会传入load()
方法
8、load()
方法
/**
* 将指定的源(sources)加载到应用上下文(ApplicationContext)中,即注册Bean定义。
*
* @param context 要加载Bean的应用上下文
* @param sources 要加载的源对象数组,可以是类、包名或配置文件路径等
*/
protected void load(ApplicationContext context, Object[] sources) {
// 如果日志处于调试模式,记录加载的源信息
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// 创建 BeanDefinitionLoader 实例,用于将源转换为 BeanDefinition 并注册到容器中
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// 如果设置了自定义的Bean名称生成器,则设置给loader使用
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
// 如果设置了资源加载器,则设置给loader使用
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
// 如果设置了环境对象,则设置给loader使用
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 执行加载操作,将源解析并注册为Bean定义
loader.load();
}
/**
* 获取与指定应用上下文关联的 BeanDefinitionRegistry。
* 如果上下文本身是 BeanDefinitionRegistry 的实例,则直接返回;
* 如果上下文是 AbstractApplicationContext 的实例,则从其内部的 BeanFactory 获取 BeanDefinitionRegistry;
* 如果无法获取,则抛出非法状态异常,表示找不到 BeanDefinitionRegistry。
*
* @param context 应用上下文,用于查找 BeanDefinitionRegistry
* @return BeanDefinitionRegistry 实例
* @throws IllegalStateException 如果无法找到 BeanDefinitionRegistry
*/
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
// 如果上下文本身就是 BeanDefinitionRegistry 的实例,直接返回
if (context instanceof BeanDefinitionRegistry registry) {
return registry;
}
// 如果上下文是 AbstractApplicationContext 类型,尝试从 BeanFactory 获取注册表
if (context instanceof AbstractApplicationContext abstractApplicationContext) {
return (BeanDefinitionRegistry) abstractApplicationContext.getBeanFactory();
}
// 否则抛出异常,表示无法定位到 BeanDefinitionRegistry
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
/**
* Load the sources into the reader.
*/
void load() {
for (Object source : this.sources) {
load(source);
}
}
/**
* 根据传入的 source 对象类型,将对应的 Bean 定义加载到应用上下文中。
* 支持的 source 类型包括:
* - Class:Java 配置类或带有注解的类
* - Resource:XML 或 Groovy 配置文件资源
* - Package:要扫描的包
* - CharSequence:字符串形式的类名、包路径或资源路径
*
* @param source 要加载的源对象
*/
private void load(Object source) {
// 参数校验:source 不能为 null
Assert.notNull(source, "'source' must not be null");
// 判断 source 类型,并调用相应的加载方法
if (source instanceof Class<?> type) {
// 如果是 Class 类型,则加载该类作为配置类或 Bean
load(type);
return;
}
if (source instanceof Resource resource) {
// 如果是 Resource 类型(如 XML 或 Groovy 文件),则加载对应的资源文件
load(resource);
return;
}
if (source instanceof Package pack) {
// 如果是 Package 类型,则扫描该包下的所有类并注册 Bean
load(pack);
return;
}
if (source instanceof CharSequence sequence) {
// 如果是字符串类型,可能是类名、包路径或资源路径,进一步解析并加载
load(sequence);
return;
}
// 如果 source 不是以上支持的类型,抛出非法参数异常
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
/**
* 加载 Class 类型的 Bean 源。
* 如果该类是 Groovy 的 DSL 配置类,则通过 GroovyBeanDefinitionReader 加载其中定义的 beans;
* 如果是普通的 Java 注解配置类,则使用 AnnotatedBeanDefinitionReader 注册为 Bean。
*
* @param source 要加载的类对象,可能是 Groovy DSL 类或 Java 注解配置类
*/
private void load(Class<?> source) {
// 如果当前环境支持 Groovy 且 source 是 GroovyBeanDefinitionSource 的子类
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// 实例化 Groovy 配置类
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
// 使用 GroovyBeanDefinitionReader 加载 DSL 中定义的 beans
((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
}
// 判断该类是否适合作为 Bean 注册(非匿名类、非 Groovy 闭包、有构造函数)
if (isEligible(source)) {
// 使用 AnnotatedBeanDefinitionReader 将该类注册为 Bean 定义
this.annotatedReader.register(source);
}
}
this.annotatedReader.register(source)
使用AnnotatedBeanDefinitionReader.register(source)
AnnotatedBeanDefinitionReader.register(source)
最终使用BeanDefinitionRegistry.registerBeanDefinition()
注册beanBeanDefinitionRegistry
接口有2个重要实现类DefaultListableBeanFactory
和GenericApplicationContext(通用的应用上下文)
GenericApplicationContext
包含DefaultListableBeanFactory
,最终GenericApplicationContext.registerBeanDefinition()
使用DefaultListableBeanFactoryt.registerBeanDefinition()
注册bean- 至此,主配置类注册到容器中,为后续
AbstractApplicationContext.refresh()
作准备,准确来说是ConfigurationClassPostProcessor
用主配置类注册所有的bean
三、注册方式
1. AnnotatedBeanDefinitionReader
- 是一个用于手动注册 Bean 类的工具类
- 可以解析类上的注解(如 @Component, @Configuration 等)并生成对应的 Bean 定义
- 和 ClassPathBeanDefinitionScanner 类似,但它不进行类路径扫描,只注册显式传入的类
2.ClassPathBeanDefinitionScanner
- 在类路径中自动扫描并注册 Bean 定义
- 基于注解的自动扫描功能,如 @ComponentScan
- 默认扫描@Component, @Repository, @Service, @Controller,以及 JSR-330 的 @Named
四、 @SpringBootApplication
注解的作用
这是一个组合注解,包含三个重要注解:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@ComponentScan
:启用组件扫描,Spring 会扫描指定包下的注解类(如@Component
,@Service
,@Repository
)并注册为 BeanDefinitions。@SpringBootConfiguration
:标记为@Configuration
@EnableAutoConfiguration
:启用自动配置机制,通过spring.factories
文件加载自动配置类。
五、 扫描组件
ComponentScan
会触发如下流程:
- 扫描指定包路径下的所有类
- 检查类是否带有
@Component
、@Service
、@Repository
、@Controller
等注解 - 将符合条件的类封装为
BeanDefinition
,并注册到BeanFactory
中
这部分由 ClassPathBeanDefinitionScanner
实现。