Spring Boot中自动配置Autoconfigure详解

在本文中,我们将讨论 spring-boot-autoconfigure 的内部结构,并学习如何创建我们自己的自动配置。最后,您将了解 Spring Boot 的魔力如何发挥作用、spring-boot-autoconfigure 模块的作用,并创建自定义自动配置(并希望能激发您创建自己的自动配置)。

什么是spring-boot-autoconfigure 
定义了Spring Boot 应用程序启动期间加载的所有自动配置的类。

这些类具有创建bean的配置,大多基于某些条件。主要条件如下:

  • 类路径上是否存在依赖项。
  • 是否定义了属性。
  • bean 是否已由用户定义。

还有更多的条件,但这些是主要的。它们是使用@Conditional注释指定的。

@AutoConfiguration(after = { HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
  matchIfMissing = true)
@Import(JpaRepositoriesImportSelector.class)
public class JpaRepositoriesAutoConfiguration {...

  • @AutoConfiguration:说明这是一个自动配置。这与 @Configuration 类似。after 表示该配置应在这两个配置之后运行。
  • @ConditionalOnBean:仅在已创建 DataSource Bean 的情况下运行此配置。
  • @ConditionalOnClass:JpaRepository 类应存在于 classpath 中,这意味着应存在 spring-data-jpa 依赖关系。
  • @ConditionalOnMissingBean:用户不应在应用程序中定义指定的 Bean。
  • @ConditionalOnProperty:spring.data.jpa.repositories.enabled 属性应为 true 或完全未定义。

满足所有这些条件后,就会创建在该类中定义的 Bean。

有些自动配置类没有 @Conditional 注解,这意味着它们是无条件配置的。例如,SslAutoConfiguration、ApplicationAvailabilityAutoConfiguration。

探索 spring-boot-autoconfigure 项目可以揭开 Spring Boot 应用程序启动过程中发生的许多 "魔法 "的神秘面纱。

创建自己的自动配置
您可以为组织中多个项目需要使用的 Bean 创建自动配置。通常情况下,这些 Bean 不是由 Spring 自动配置的,而是您的项目根据前面讨论的条件(如类路径上的依赖关系或属性值)而需要的。

让我们尝试自动配置 rxjava3-jdbc 数据库 Bean。第一步是创建一个具有自动配置的项目,然后将其作为依赖关系添加到其他项目中。因此,让我们创建一个包含 rxjava3-jdbc 和 spring-boot-autoconfigure 依赖项的 maven 项目。

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.davidmoten</groupId>
            <artifactId>rxjava3-jdbc</artifactId>
            <version>0.1.4</version>
            <optional>true</optional>
        </dependency>


请注意,rxjava3-jdbc 依赖关系是可选的,因为我们希望用户自己决定是否要在其项目中使用该依赖关系。如果用户使用了该依赖关系,则条件将匹配并创建 Bean(想想 @Conditional)。

现在是创建自定义自动配置类的时候了:

@AutoConfiguration
@ConditionalOnClass(Database.class)
@EnableConfigurationProperties(RxJava3JdbcDatabaseProperties.class)
public class RxJava3JdbcDatabaseAutoConfiguration {

    @Bean
    public Database database(RxJava3JdbcDatabaseProperties properties) {
        return Database.nonBlocking()
                .url(properties.getUrl())
                .user(properties.getUsername())
                .password(properties.getPassword())
                .maxPoolSize(properties.getMaxPoolSize())
                .build();
    }
}

请注意 @AutoConfiguration 注解和用于检查 rxjava3-jdbc 依赖关系中的数据库类是否在 classpath 上的 @Conditional。如果这些条件匹配,它就会将一些自定义属性映射到 RxJava3JdbcDatabaseProperties 类,并创建数据库 Bean。

@ConfigurationProperties(prefix = "rxjava3.jdbc.database")
public class RxJava3JdbcDatabaseProperties {
    private String username;
    private String password;
    private String url;
    private int maxPoolSize;

//getters and setters
...

最后一步是在 src/main/resources/META-INF/spring 下创建一个名为 org.springframework.boot.autoconfigure.AutoConfiguration.imports 的新文件,并添加自动配置类的全限定名称:

com.sandeeprai.springboot.autoconfigure.data.rxjava.RxJava3JdbcDatabaseAutoConfiguration

将此项目添加为另一个项目的依赖项,以查看自动配置的工作情况。

实际操作
要使用自动配置类,请在上述项目中运行 mvn clean install(将其添加到本地 .m2 资源库),然后在另一个项目中导入依赖关系。

demo project 创建了一个演示项目,并添加了上述自动配置依赖项和 rxjava3-jdbc,后者包含需要自动配置的数据库类:

 <dependency>
   <groupId>com.github.davidmoten</groupId>
   <artifactId>rxjava3-jdbc</artifactId>
   <version>0.1.4</version>
  </dependency>

  <dependency>
   <groupId>com.sandeeprai</groupId>
   <artifactId>sandeeprai-spring-boot-autoconfigure</artifactId>
   <version>1.1.0</version>
  </dependency>


主类是一个简单的 Spring Boot 应用程序主类:

@SpringBootApplication
public class SpringbootAutoconfigureDemoApplication {

 public static void main(String[] args) {
  SpringApplication.run(SpringbootAutoconfigureDemoApplication.class, args);
...

在 application.properties 中添加配置数据库 Bean 所需的自定义属性:

rxjava3.jdbc.database.username=sa
rxjava3.jdbc.database.password=sa
rxjava3.jdbc.database.url=jdbc:h2:mem:
rxjava3.jdbc.database.max-pool-size=10

这将配置一个连接到内存 h2 数据库的数据库 bean。您还需要 h2 依赖关系才能完全运行。

现在使用 --debug 标志启动应用程序,在控制台中查看 Spring Boot 的自动配置条件评估报告:

============================
CONDITIONS EVALUATION REPORT
============================


Positive matches:
-----------------

   ApplicationAvailabilityAutoConfiguration#applicationAvailability matched:
      - @ConditionalOnMissingBean (types: org.springframework.boot.availability.ApplicationAvailability; SearchStrategy: all) did not find any beans (OnBeanCondition)
...

   RxJava3JdbcDatabaseAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.davidmoten.rxjava3.jdbc.Database' (OnClassCondition)
...

Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'jakarta.jms.ConnectionFactory' (OnClassCondition)
...

Exclusions:
-----------

    None


Unconditional classes:
----------------------

    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
...

这将列出创建的每个自动配置 Bean、未创建的 Bean、每个正负匹配的原因、排除项和无条件自动配置的 Bean。由于我们包含了 rxjava3-jdbc 依赖项,因此正匹配列表中包括了我们的自定义自动配置 Bean。如果移除依赖关系并重新运行项目,它应该会出现在否定匹配部分。


问:我应该在自动配置类上使用 @AutoConfiguration 还是 @Configuration 注解?
答:根据 Spring Boot 的发布说明,@AutoConfiguration 应用于注解顶级自动配置类。注解本身并不会使类成为自动配置的候选类。您仍然需要将其添加到前面提到的 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中。在这方面,它与 @Configuration 类似(javadoc 也说明了这一点:除了 ConfigurationproxyBeanMethods() proxyBeanMethods 始终为 false 之外,自动配置类是常规的 @Configuration)。不过,@AutoConfiguration 在此上下文中更有意义,它是目前的标准,并明确表示该类是用于自动配置的。

问:我应该将自定义自动配置类添加到 org.springframework.boot.autoconfigure.AutoConfiguration.imports 还是 spring.factories 文件中?
答:尽管许多教程都提到要使用 spring.factories 文件,但根据 Spring Boot 的发布说明,该文件已被弃用,而应使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。

问:何时应创建和使用自定义自动配置?
答:如果 Spring Boot 不会自动配置某个 Bean,而该 Bean 在您组织的多个项目中使用,并且需要根据特定条件进行配置,则应创建自定义自动配置。对于更简单的情况,可以在应用程序代码中使用 @Configuration 类。

​​​​​​​源码:GitHub.