Spring Boot Auto-configurations
My team and I make heavy use of Spring Boot to build microservices that power our applications at our company. We've even gone so far as to write our own custom auto configurations to ease development. We package these auto configurations along with other goodies into what we call our platform.
Seemingly Random Application Startup Failures
Recently, after releasing a new platform version, we encountered an issue where one of our microservices would fail to start at what appeared to be random intervals. After further digging, we noticed specifically that our application would start error-free when ran via mvn spring-boot:run but would fail with an obscure error when ran with java -jar.
Puzzled, we hooked up a debugger to each way of executing the application and noticed something very interesting: our auto configurations were executed in a different order when executed with Maven vs java -jar.
No problem we thought, we'll just add @Ordered annotations to our auto configurations to define an explicit ordering. After applying the changes, our problem appeared to be solved ... until we realized that now the application wouldn't start if ran it within IntelliJ.
Clearly the @Ordered annotation didn't work and we still had an ordering problem. We dug even further and eventually located this blurb in the Spring Boot 1.3 release notes:
Spring Boot no longer honours @Order to manage ordering between auto-configuration classes, please use @AutoconfigureOrder instead. Note also that you can use @AutoconfigureBefore and @AutoconfigureAfter to specify the order against specific auto-configuration classes.
Ah ha! This was definitely our problem! One small change from @Ordered to @AutoConfigurationAfter and we had our application starting in all cases.
Retrospective: Why Does Order Matter?
The ordering in which @Configuration classes are loaded usually isn't of any concern and you've probably never needed to worry about it. You annotate your classes with @ComponentScan and Spring Boot picks up the @Configuration classes and everything works great.
However, once you introduce @ConditionalOnX annotations like @ConditionalOnBean or @ConditionalOnMissingBean, ordering becomes very important. Each of these conditional annotations implicitly relies on a defined ordering. If ordering is undefined you may run into cases, like we did, where a conditional piece of configuration is executed inconsistently.
- Auto configurations should never be included via @ComponentScan because ordering cannot be guaranteed.
- Auto configurations should live in a different package to avoid being accidentally picked up by @ComponentScan.
- Auto configurations should be declared in a META-INF/spring.factories and should NOT be subject to @ComponentScan as mentioned above.
- @Ordered does not apply to @Configuration classes since Spring Boot 1.3.
- Use @AutoConfigureOrder, @AutoConfigureBefore, and @AutoConfigureAfter to order auto configurations for Spring Boot 1.3 or greater.
- Avoid using @ConditionalOnX annotations outside of auto-configurations. @ConditionalOnX annotations are sensitive to ordering and ordering cannot be guaranteed with just @Configuration classes.