Spring Boot auto configuration is great! No doubt about it, it has simplified my life. Recently while working on a new project, it appeared as though Spring Boot auto configuration was playing some dirty tricks on me. TL;DR, the problem was not with Spring Boot, but rather with me.
Why are you here?
To give you some context, I have a commonly used set of auto configuration classes that I
depend on in several of my Spring Boot projects. However, not all the auto configuration
classes are required by all my projects. To ensure they are not all instantiated, they
are all annotated with
@Profile so that I can simply pass
when launching my application and only the matching auto configuration classes are instantiated.
Beware of common package hierarchies
In my bundle of auto configuration classes, I have a root package name, something like
com.example. Below that I have several other packages, separating feature concerns. When I create a new Spring Boot project that depends on this bundle, it often shares the same top level package but my application class; the one annotated
@SpringBootApplication is not in the top level package. It is in a sub-package. Something like
com.example.myapp.Application. Life up until this point has been hunky-dory.
I encountered trouble today when building a new Spring Boot project, which depends on my bundle of auto configuration classes. This is a project I created through IntelliJ using the Spring Initializr project type. When I first launched the application, it started complaining about missing classes and wouldn’t launch. Scratching my head a bit, I thought, I forgot to pass the active profiles when launching from IntelliJ. So I fixed that and restarted the app. Yet again I was faced with the same missing class exceptions. So I said ok, I’ll add that missing dependency even though I know I shouldn’t need to.
Restarting the app this time, I started facing a bunch of unsatisfied
@Bean dependencies in components that my application wasn’t depending on. Those beans should only be getting instantiated when a certain profile is active; which it wasn’t.
After a lot of searching and more head scratching, I found the problem. My cool new application has the application class in the same top level package as my bundle of auto configuration classes. So basically something like
com.example.Application. Since that class is annotated
@SpringBootApplication then component scanning was catching all of my bundled auto configuration classes. Refactoring the application class to
com.example.mycoolnewapp.Application solved the problem. Life as I know it was back to normal.
Maybe I’m going about this wrong. You might say I should be doing this stuff with microservices. For the most part I try to, but some of my JPA relationships just don’t make sense as separate microservices. They are too interrelated and separating them would introduce a fine grained architecture that would be more cumbersome to maintain than what it offers in benefits.