Spring Boot auto-configuration order

前端 未结 3 2050
广开言路
广开言路 2021-01-01 07:14

I want to create a Spring Boot auto-configuration class that (conditionally) creates a single bean A. The challenge however is, that this been has to be created

相关标签:
3条回答
  • 2021-01-01 07:44

    There is a new annotation @AutoConfigureOrder in Boot 1.3.0. Though it is unclear to me at least if this would still behave the same way as @AutoConfiugreBefore.

    0 讨论(0)
  • 2021-01-01 07:53

    Refer to @hzpz 's answer, here is a example for create database before the auto-configuration of Hikari data source.

    import com.zaxxer.hikari.HikariDataSource;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    import javax.annotation.PostConstruct;
    import org.springframework.beans.factory.BeanFactoryUtils;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.util.StringUtils;
    
    @Configuration
    public class CreateDatabaseConfig {
    
      @Value("${spring.datasource.url}")
      private String url;
      @Value("${spring.datasource.hikari.username}")
      private String username;
      @Value("${spring.datasource.hikari.password}")
      private String password;
      @Value("${spring.datasource.hikari.catalog}")
      private String catalog;
    
      @Bean
      public static BeanFactoryPostProcessor dependsOnPostProcessor() {
        return beanFactory -> {
          String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
              beanFactory, HikariDataSource.class, true, false);
          for (String beanName : beanNames) {
            BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
            definition.setDependsOn(StringUtils.addStringToArray(
                definition.getDependsOn(), "createDatabaseConfig"));
          }
        };
      }
    
      @PostConstruct
      public void init() {
        try (
            Connection connection = DriverManager.getConnection(url, username, password);
            Statement statement = connection.createStatement()
        ) {
          statement.executeUpdate(
              "CREATE DATABASE IF NOT EXISTS `" + catalog
                  + "` DEFAULT CHARACTER SET = utf8mb4 COLLATE utf8mb4_unicode_ci;"
          );
        } catch (SQLException e) {
          throw new RuntimeException("Create Database Fail", e);
        }
      }
    }
    

    More initialization for schema and data can be done with data.sql and schema.sql, see 85. Database Initialization
    ps. I tried to CREATE DATABASE in schema.sql but failed, and the example above works :)

    0 讨论(0)
  • 2021-01-01 08:00

    It turns out, what I want is to dynamically make instances of B depend on A. This can be achieved by using a BeanFactoryPostProcessor to alter the bean definitions of B beans.

    public class DependsOnPostProcessor implements BeanFactoryPostProcessor {
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    beanFactory, B.class, true, false);
            for (String beanName : beanNames) {
                BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
                definition.setDependsOn(StringUtils.addStringToArray(
                        definition.getDependsOn(), "beanNameOfB");
            }
        }
    
    }
    

    This works with plain Spring, no Spring Boot required. To complete the auto-configuration I need to add the bean definition for the DependsOnPostProcessor to the configuration class that instantiates the bean A.

    0 讨论(0)
提交回复
热议问题