文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Java 框架 SpringBoot 自动装配原理

一、自动装配要解决什么问题?

在传统 Spring 应用中,我们需要大量编写 XML 配置或 Java @Configuration 类来声明 Bean 及其依赖关系(如数据源、事务管理器、MVC 组件等)。这个过程繁琐且容易出错。

自动装配的目标是:根据项目 classpath 中存在的依赖 Jar 包,自动推断出你需要哪些功能,并为你自动配置好这些功能所需的 Bean。例如:

  • classpath 中存在 H2 数据库的 Jar 时,自动配置一个内存数据源。
  • classpath 中存在 Spring MVC 的 Jar 时,自动配置 DispatcherServletViewResolver 等组件。
  • classpath 中存在 spring-boot-starter-data-jpa 时,自动配置 JPA EntityManagerFactoryTransactionManager 等。

这一切都得益于三个核心注解、条件化配置和一种强大的发现机制。


二、核心注解:@SpringBootApplication

一切始于主类上的 @SpringBootApplication 注解。它是一个复合注解,由三个核心注解组成:

@SpringBootConfiguration // 本质是 @Configuration,标记该类为配置类
@EnableAutoConfiguration // ★ 启用自动配置的核心注解 ★
@ComponentScan(          // 开启组件扫描,定位 @Component, @Service, @Controller 等
    basePackages = {"com.example"}
)
public @interface SpringBootApplication {
    // ...
}

其中,@EnableAutoConfiguration 是开启自动装配大门的钥匙。


三、自动装配的核心流程

@EnableAutoConfiguration 注解的关键是导入了一个选择器:AutoConfigurationImportSelector

@Import(AutoConfigurationImportSelector.class) // ★ 导入选择器 ★
public @interface EnableAutoConfiguration {
    // ...
}

整个自动装配的过程,特别是 AutoConfigurationImportSelector 的工作流程,可以通过以下流程图清晰地展现:

@SpringBootApplication@EnableAutoConfigurationAutoConfigurationImportSelectorSpringFactoriesLoaderAuto-Configuration Classes (e.g., DataSourceAutoConfiguration)@Conditional Annotations (OnClass, OnBean, etc.)Spring ApplicationContext复合注解包含@Import 触发加载1. 加载候选配置loadFactoryNames(EnableAutoConfiguration.class)读取所有 jar 包中的 META-INF/spring.factories 文件返回所有 EnableAutoConfiguration 对应的值 (e.g., org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration)2. 筛选与去重去重、排除(exclude属性)、过滤(根据 AutoConfigurationImportFilter)3. 条件注解过滤 (最关键的一步)评估条件注解 (e.g., @ConditionalOnClass(DataSource.class))条件是否满足?loop[For each auto-configuration class]最终形成有效的自动配置类列表4. 导入并处理将有效的自动配置类导入容器,作为普通的 @Configuration 类处理5. 初始化上下文解析有效的自动配置类@Bean 方法被执行,符合条件的 Bean 被注册到容器中最终结果:一个根据当前环境和依赖定制好的应用上下文@SpringBootApplication@EnableAutoConfigurationAutoConfigurationImportSelectorSpringFactoriesLoaderAuto-Configuration Classes (e.g., DataSourceAutoConfiguration)@Conditional Annotations (OnClass, OnBean, etc.)Spring ApplicationContext
流程步骤解析:
  1. SpringFactoriesLoader.loadFactoryNames()

    • 这个工具类会扫描 classpath 下所有 Jar 包中的 META-INF/spring.factories 文件。
    • AutoConfigurationImportSelector 请求加载 EnableAutoConfiguration 这个 key 对应的所有值。
    • spring-boot-autoconfigure.jar 是这个机制的核心。它的 META-INF/spring.factories 文件定义了所有官方支持的自动配置类的列表(超过 100 个),例如:
      # spring-boot-autoconfigure-xxx.jar/META-INF/spring.factories
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
      org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
      org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
      org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
      org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
      ... (非常长的列表)
      
  2. 筛选与去重

    • 获取到所有候选配置类后,SpringBoot 会进行去重。
    • 并根据 @EnableAutoConfigurationexcludeexcludeName 属性排除用户明确指定的配置。
  3. 条件注解过滤 (最关键的步骤)

    • 列表中的每一个自动配置类(如 DataSourceAutoConfiguration)都被大量的 @Conditional 派生注解所修饰
    • SpringBoot 会依据这些条件注解,逐一对候选配置类进行判断。只有所有条件都满足的配置类,才会被真正导入和生效
  4. 导入容器

    • 经过层层过滤后,最终有效的自动配置类会被当作普通的 @Configuration 类一样,由 Spring 容器加载。
    • 这些配置类内部使用 @Bean 注解定义了一系列 Bean。这些 @Bean 方法同样受条件注解的控制
  5. Bean 的注册

    • 自动配置类中符合条件的 @Bean 方法会被执行,其返回的对象被注册到 IoC 容器中,成为我们可以直接 @Autowired 使用的 Bean。

四、条件注解:自动装配的“大脑”

条件注解是 SpringBoot 自动装配的灵魂,它决定了“在什么条件下配置才生效”。它们位于 org.springframework.boot.autoconfigure.condition 包下。

注解生效条件
@ConditionalOnClassclasspath 下存在指定的类时生效。
@ConditionalOnMissingClassclasspath 下不存在指定的类时生效。
@ConditionalOnBeanSpring 容器中存在指定类型的 Bean 时生效。
@ConditionalOnMissingBean这是最常用的注解之一。Spring 容器中不存在指定类型的 Bean 时生效。这为用户自定义 Bean 提供了覆盖自动配置的机会。
@ConditionalOnProperty指定的配置属性有特定的值时生效。
@ConditionalOnResourceclasspath 下存在指定的资源文件时生效。
@ConditionalOnWebApplication当前应用是 Web 应用时生效。
@ConditionalOnNotWebApplication当前应用不是 Web 应用时生效。
@ConditionalOnJndi指定的 JNDI 位置存在时生效。
示例:DataSourceAutoConfiguration

让我们看一个经典的例子,它展示了条件注解如何精妙地工作:

@Configuration(proxyBeanMethods = false) // 表明这是一个配置类
// 条件1: 必须在classpath下找到DataSource.class和EmbeddedDatabaseType.class
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
// 条件2: 必须在classpath下找到Spring的JdbcTemplate类
@ConditionalOnMissingBean(type = "org.springframework.jdbc.core.JdbcOperations")
// 条件3: 必须存在spring.datasource.*的相关配置属性
@ConditionalOnProperty(prefix = "spring.datasource", name = {"name"}, matchIfMissing = true)
public class DataSourceAutoConfiguration {

    // 这个配置在classpath下有H2、HSQL等内嵌DB驱动时才生效
    @Configuration(proxyBeanMethods = false)
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
    protected static class EmbeddedDatabaseConfiguration {
        @Bean
        @ConditionalOnMissingBean(name = {"dataSource"}, type = {DataSource.class})
        public DataSource dataSource(DataSourceProperties properties) {
            // 使用配置的属性构建一个内嵌数据源
            return new EmbeddedDataSourceBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .build();
        }
    }

    // 这个配置在classpath下有Tomcat JDBC连接池时才生效
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
    @ConditionalOnMissingBean({DataSource.class, XADataSource.class})
    @ConditionalOnProperty(prefix = "spring.datasource", name = {"type"}, havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
    protected static class TomcatDataSourceConfiguration {
        @Bean
        public DataSource dataSource(DataSourceProperties properties) {
            // 构建一个Tomcat JDBC连接池
            TomcatDataSource dataSource = new TomcatDataSource();
            dataSource.setUrl(properties.getUrl());
            dataSource.setUsername(properties.getUsername());
            // ... 更多配置
            return dataSource;
        }
    }
}

逻辑是

  1. 如果我没提供自己的 DataSource Bean (@ConditionalOnMissingBean),并且…
  2. classpath 里有内嵌数据库驱动(如 H2),那么 SpringBoot 就自动给我配一个内嵌 DataSource
  3. 如果 classpath 里有 Tomcat JDBC 连接池,并且配置了 type,那么 SpringBoot 就自动给我配一个 Tomcat 连接池。
  4. 如果我手动在自定义配置类里 @Bean 了一个 DataSource,那么因为 @ConditionalOnMissingBean 条件不满足,SpringBoot 的自动配置就会跳过,从而实现了“覆盖”。

五、总结与最佳实践

自动装配原理总结

  1. 启动@SpringBootApplication -> @EnableAutoConfiguration
  2. 加载AutoConfigurationImportSelector 通过 SpringFactoriesLoader 加载所有 META-INF/spring.factories 中定义的自动配置类。
  3. 过滤:利用大量的条件注解 (@ConditionalOnXxx),根据当前环境(classpath、已存在的 Bean、配置属性等)对配置类进行筛选。
  4. 装配:最终生效的配置类向容器中注入预先设计好的、满足当前应用场景的 Bean。

最佳实践

  • 查看自动配置详情:使用 --debug 模式启动应用 (java -jar myapp.jar --debug),SpringBoot 会打印出所有自动配置类的评估报告(哪些生效,哪些未生效及其原因)。
  • 覆盖自动配置:只需手动在你的 @Configuration 类中定义一个 Bean(如 DataSource),即可覆盖自动配置提供的默认 Bean。
  • 通过属性微调:绝大多数自动配置的 Bean 都提供了相应的配置属性(以 spring.* 开头),可以在 application.properties 中修改其行为,而无需覆盖整个 Bean。
  • 排除特定自动配置:如果明确不需要某项自动配置,可以使用 @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) 将其关闭。
posted @ 2025-09-11 09:29  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源