Spring boot 加载和注册 BeanDefinition

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 定义来源。

初始化逻辑说明:

  1. resourceLoader:传入的资源加载器被保存下来,在后续加载 Bean 定义时可能会用到。
  2. primarySources:主配置类集合被转换为 LinkedHashSet 以保持插入顺序且避免重复,在最后的run()会用到
  3. webApplicationType:通过类路径自动推断是否是 Web 应用、响应式 Web 应用还是普通应用。
  4. bootstrapRegistryInitializers:从 spring.factories 文件中加载所有 BootstrapRegistryInitializer 实现类并初始化。
  5. initializers:从 spring.factories 中获取所有的 ApplicationContextInitializer 并设置到当前实例。
  6. listeners:从 spring.factories 中获取所有的 ApplicationListener 并设置到当前实例。
  7. 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()注册bean
  • BeanDefinitionRegistry接口有2个重要实现类DefaultListableBeanFactoryGenericApplicationContext(通用的应用上下文)
  • 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 实现。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值