2

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 before another bean B is created in one of Spring Boot's default auto-configuration classes. The bean B does not depend on A.

My first try was to use @AutoConfigureBefore. That didn't work the way I expected and judging from this comment by Dave Syer it shouldn't.

Some background: The aforementioned bean A alters a Mongo Database and this has to happen before MongoTemplate is created.

Community
  • 1
  • 1
hzpz
  • 7,536
  • 1
  • 38
  • 44

3 Answers3

3

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.

hzpz
  • 7,536
  • 1
  • 38
  • 44
0

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.

jst
  • 1,697
  • 1
  • 12
  • 13
  • Thanks for the info. But from what I can gather from [the code](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationSorter.java), `@AutoConfigureOrder` is just another means to order auto-configurations, not the instantiation of beans they create. – hzpz Jul 29 '15 at 17:48
0

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 :)

Yin
  • 51
  • 1
  • 4