延缓Spring Boot启动时间直到数据库启动的方法和源码 - Marten


当使用Spring Boot或仅使用普通的Spring Framework时,可能要延迟应用程序的启动,直到可以与数据库建立正确的连接为止。当使用容器技术(例如Docker)时,情况可能更是如此。
DatabaseStartupValidator会延迟应用程序的进一步启动,直到可以建立与数据库的连接为止。默认情况下,它将每秒尝试连接到数据库,并仅捕获异常。它将尝试60秒,此后如果无法建立连接,则失败(所有这些属性都是可配置的)。
要使用它,只需声明一个bean并注入数据源。您定义一个验证查询(从Spring 5.3开始,它将isValid默认使用JDBC 4 方法!)。Spring Boot附带了一个方便的枚举,该枚举已经包含针对一系列受支持数据库的默认验证查询(由Spring Boot Actuator中的运行状况检查使用)。

@SpringBootApplication
public class DatabaseUpApplication {

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

    @Bean
    public DatabaseStartupValidator databaseStartupValidator(DataSource dataSource) {
        var dsv = new DatabaseStartupValidator();
        dsv.setDataSource(dataSource);
        dsv.setValidationQuery(DatabaseDriver.POSTGRESQL.getValidationQuery());
        return dsv;
    }
}

但是,仅定义此bean可能还不够,需要更多配置。有许多依赖DataSource 的beans如EntityManagerFactory,Flyway 或 JdbcTemplate ,需要手工增加@DependsOn在这些@Bean方法上:可通过Spring Boot进行一些自动配置,也可以使用a BeanFactoryPostProcessor来修改bean

@Bean
public static BeanFactoryPostProcessor dependsOnPostProcessor() {
    return bf -> {
        // Let beans that need the database depend on the DatabaseStartupValidator
       
// like the JPA EntityManagerFactory or Flyway
        String[] flyway = bf.getBeanNamesForType(Flyway.class);
        Stream.of(flyway)
                .map(bf::getBeanDefinition)
                .forEach(it -> it.setDependsOn(
"databaseStartupValidator"));

        String[] jpa = bf.getBeanNamesForType(EntityManagerFactory.class);
        Stream.of(jpa)
                .map(bf::getBeanDefinition)
                .forEach(it -> it.setDependsOn(
"databaseStartupValidator"));
    };
}

这里延缓了Flyway 和 EntityManagerFactory执行,直到DatabaseStartupValidator 完全初始化。

源代码可以在GitHub找到