Spring Boot 自动配置中@EnableAutoConfiguration 与 exclude 内部到底发生了什么?

Spring Boot 中的自动配置类是什么?

Spring Boot 中的自动配置 (Autoconfiguration) 是一个非常强大的特性,它会根据你添加到项目类路径 (classpath) 下的依赖,来自动配置你的 Spring 应用程序。它通过“猜测”你可能需要什么,并为你自动进行配置,从而帮助开发者避免了大量冗余的样板式配置代码。

自动配置类 (Autoconfiguration Classes) 是 Spring Boot 中一些特殊的类(通常带有 @Configuration 注解和各种 @Conditional 条件注解),它们能够自动地定义 Bean 和相关的配置。

这些类都位于 spring-boot-autoconfigure.jar 这个模块中。

例如,如果你在项目的类路径下添加了 Spring Data JPA 的依赖,Spring Boot 就会自动为你配置好 DataSource(数据源)、EntityManagerFactory(实体管理器工厂)以及事务管理器(transaction manager),而你完全不需要手动编写这些配置。

这个机制早期是通过一个名为 spring.factories 的文件来工作的(在较新的 Spring Boot 版本中,更推荐使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,但 spring.factories 仍为向后兼容而保留支持)。Spring Boot 在这个文件中列出了所有需要被自动加载的自动配置类。


如何在 Spring Boot 中排除某个自动配置类?

有时候,默认的自动配置可能并不适合你的应用场景,或者它可能与你自定义的配置产生冲突。Spring Boot 允许你排除特定的自动配置类

有以下几种排除自动配置的方法:

  1. 1. 使用 @SpringBootApplication 注解的 exclude 属性:
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    
    // 排除 DataSourceAutoConfiguration,这样 Spring Boot 就不会自动配置数据源了
    @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        }
    }
  2. 2. 使用 application.properties 文件:
    # 在 application.properties 中排除
    spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  3. 3. 或者在 application.yml 文件中:
    spring:
      autoconfigure:
        exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

这个排除机制内部是如何工作的呢?

当 Spring Boot 启动时,它会扫描 spring-boot-autoconfigure.jar 包内的 spring.factories 文件(或新的 AutoConfiguration.imports 文件),找到所有可用的自动配置类。
这些类会根据类路径中是否存在特定依赖、配置文件中是否有特定属性以及其他条件(通过各种 @Conditional 注解来判断),来有条件地加载

当你指定了要排除的类(例如,通过 @SpringBootApplication 的 exclude 属性),Spring Boot 会在应用这些自动配置之前,先将你指定的这些类从候选列表中移除掉。

这样就能阻止被排除的自动配置类加载它们所定义的 Bean 和配置,从而让你的自定义配置或手动配置能够优先被应用。


太棒了!现在,让我们深入探讨一下 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) 在 Spring Boot 内部究竟是如何一步步工作的 —— 揭开它幕后的秘密。

@SpringBootApplication(exclude = ...) 的内部工作原理

第一步:@SpringBootApplication 是什么?

@SpringBootApplication

这是一个元注解 (meta-annotation),它实际上整合了以下三个核心注解的功能:

@Configuration // 声明该类是一个配置类
@EnableAutoConfiguration // 启用 Spring Boot 的自动配置机制
@ComponentScan // 启用组件扫描,自动发现并注册 Bean

所以,从内部来看,下面这行代码:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })

就等同于:

@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }) // 排除属性被传递给了 @EnableAutoConfiguration
@ComponentScan

这里的关键角色就是 @EnableAutoConfiguration

第二步:@EnableAutoConfiguration 的作用
@EnableAutoConfiguration 注解通过一个名为 AutoConfigurationImportSelector 的类来触发 Spring Boot 的自动配置机制

它在内部大致会做这些事情:

  1. 1. 查找所有定义在 META-INF/spring.factories 文件(或较新版本中的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)中的自动配置类。spring.factories 文件中的一个示例条目可能如下:
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
    org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
    ... # 其他自动配置类
  2. 2. Spring Boot 使用 SpringFactoriesLoader 工具类来加载这些自动配置类的全限定名。

第三步:当你使用 exclude = { DataSourceAutoConfiguration.class } 时会发生什么?

当你写下:

@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })

在内部:

  1. 1. Spring Boot 从 spring.factories 文件中读取所有可用的自动配置类列表。

  2. 2. 在应用程序启动过程中,AutoConfigurationImportSelector 类会被调用。

  3. 3. 它获取到所有候选的自动配置类列表,准备加载它们。

  4. 4. 在真正导入(加载)这些自动配置类之前,它会检查 @EnableAutoConfiguration 注解的 exclude 属性,并将属性中指定的类从候选列表中移除

  5. 5. 因此,在这个例子中,DataSourceAutoConfiguration.class 就会被过滤掉根本不会被加载

这就确保了 Spring Boot 不会注册任何与 DataSourceAutoConfiguration 类相关的 Bean 或配置(比如默认的 DataSourceJdbcTemplate 等)。

可视化流程图 (Visual Flow)

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
        |
        V
@EnableAutoConfiguration(exclude = ...) // 排除信息传递到这里
        |
        V
AutoConfigurationImportSelector // 自动配置导入选择器被触发
        |
        V
从 spring.factories (或新机制) 加载候选自动配置类列表
        |
        V
过滤掉 DataSourceAutoConfiguration (以及其他被排除的类)
        |
        V
只有剩余的自动配置类被导入和处理

真实场景案例:自定义多租户数据库设置

场景
你正在构建一个 SaaS (软件即服务) 平台,其中每个客户(即租户 tenant)都拥有自己独立的数据库 schema(模式)。关键需求如下:

  1. 1. 两个不同的 DataSource (数据源)

    • • 主数据源 (Master DataSource) — 连接到一个“租户目录”数据库,该库存储元数据(如租户 ID -> schema 名称、账单信息、功能开关等)。

    • • 租户路由数据源 (Tenant Routing DataSource) — 一个特殊的 AbstractRoutingDataSource 实现,它会针对每一个传入的请求,查询主数据源以获取当前租户 ID,然后动态地将数据库操作路由到该租户对应的正确 schema。

  2. 2. 精细的事务规则 — 你必须能够在主数据源上开启只读事务(用于查询租户信息),并在租户的 schema 上开启独立的读写事务(用于业务操作)。

  3. 3. Flyway /Liquibase 数据库迁移脚本必须按每个 schema 单独运行,而不是在单个默认的 schema 上运行。

如果你让 Spring Boot 默认的 DataSourceAutoConfiguration 生效:

  • • 它会基于配置文件中唯一的一组 spring.datasource.* 属性,急切地创建一个单一的 DataSource Bean

  • • 它同时还会自动配置一个默认的 JdbcTemplatePlatformTransactionManager,以及(如果 Flyway 在类路径上)一个指向同一个数据库的单一 Flyway 迁移任务。

  • • 在这种情况下,你的自定义租户路由逻辑将永远不会被执行,并且 Flyway 可能会错误地在主数据库上运行所有租户的迁移脚本,从而损坏主数据库

解决方案:排除并替换

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
// 可能还需要排除 HibernateJpaAutoConfiguration, JpaRepositoriesAutoConfiguration 等,取决于你的具体需求
// import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;

@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class
    // HibernateJpaAutoConfiguration.class // 如果你也想完全自定义 JPA 设置
})
publicclassSaasPlatformApplication {
    publicstaticvoidmain(String[] args) {
        SpringApplication.run(SaasPlatformApplication.class, args);
    }
    // 在这里或其他的 @Configuration 类中,你需要手动定义你的主数据源、租户路由数据源、
    // 对应的 JdbcTemplate、事务管理器,以及自定义的 Flyway/Liquibase 迁移策略。
}

在排除了 DataSourceAutoConfiguration 之后,Spring Boot 就不会再创建默认的 DataSourceJdbcTemplateJpaTransactionManager(如果 JPA 相关自动配置也被排除了的话)以及默认的 Flyway Bean。

然后,你就需要自己提供自定义的 Bean

  • • 数据源 (DataSource):定义你的主数据源 Bean 和租户路由数据源 (AbstractRoutingDataSource) Bean。

  • • 事务管理器 (PlatformTransactionManager):可能需要为不同的数据源配置不同的事务管理器,或者一个能处理动态数据源的事务管理器。

  • • Flyway/Liquibase:创建一个自定义的 FlywayMigrationStrategy (或类似的 Liquibase 策略),让它能够遍历所有已知的租户 schema,并分别在每个 schema 上执行迁移脚本。

  • • 安全/过滤器 (Security / Filters):通常会放置一个 OncePerRequestFilter(或类似的机制)在请求处理链的前面,用来从请求(如 JWT、HTTP Header 或子域名)中捕获租户 ID,并将其存储到一个 TenantContext(通常使用 ThreadLocal 实现)中,供后续的租户路由数据源使用。

成果

  • • 实现了真正的、在 schema 级别进行隔离的多租户架构。

  • • 可以为每个租户实现零停机时间的数据库迁移

  • • 由于所有的基础设施相关的 Bean(如事务管理器、DAO 等)都使用了你自定义的租户路由逻辑,因此不会发生意外的跨租户数据泄露

这种模式广泛应用于各种 SaaS 产品,如电子商务平台、内容管理系统 (CMS)、商业智能分析仪表盘等,这些场景下每个客户的数据都必须在逻辑上(有时甚至是物理上)进行隔离。通过排除像 DataSourceAutoConfiguration 这样的默认自动配置,你就能用一个为复杂、真实世界需求量身定制的精细设计来取代 Spring Boot 提供的那些“一刀切”的合理默认配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java干货

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值