17 May 2014

Spring boot: @DependsOn is not enough anymore

Spring boot can really help speed up starting new project. Ten lines of java configuration (mostly generated by let's say data-jpa-mvn archetype) and you are ready to write your business logic. All the configuration changes can be postponed until you really need them. But when you start doing those changes, very quickly you may see that something is reeeeeally missing.

Let's say we want to use beans A and C created by spring boot. But also we want to create our own bean B that should be initialized in between. The real word scenario is for example: default DataSource, default EntityManagerFactory and customized Flyway service which must run before hibernate. We can easily say flyway should depend on the datasource - @Autowired does the trick. But how can we say that hibernate should depend on our flyway? We can't add anything to the hibernate bean because we don't declare it. So what's the solution? First let's check how and where spring declares the hibernate. After listing *AutoConfiguration classes we see HibernateJpaAutoConfiguration and later, in its superclass, we can find:
@Bean
@ConditionalOnMissingBean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
                           JpaVendorAdapter jpaVendorAdapter) {...}
So one way of enforcing the order is:
@Configuration
class FlywayConfig extends HibernateJpaAutoConfiguration {

   @Autowired Flyway flyway;
}
And if we need more precise control, we can override the bean:
@Configuration
class FlywayConfig extends HibernateJpaAutoConfiguration {
   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory(
                                   JpaVendorAdapter jpaVendorAdapter,
                                   Flyway flyway) {
      return super.entityManagerFactory(jpaVendorAdapter);
   }
}
Used versions: spring 4.0.3.RELEASE, spring-boot 1.0.2.RELEASE