My witty-title-generator was a bit off for this one. The pun does not imply that Spring is broken, not that it has broken anything. But what I’m going to describe is bound to shake (and break?) a misconception around it.
Consider Spring JavaConfig (now officially part of the Spring distribution). I admit it’s not my favorite configuration option (and – both – regular readers know I have strong opinions on DI). But many people prefer it to XML configuration. And some of them believe something about it, which is not true.
Classes annotated with @Configuration
are scanned for bean declarations. At the same time, this annotation also creates a bean for the class. So far, so good. Throw @DependsOn
to the picture, which can be used to inform Spring about a dependency which cannot be established through the regular means of constructor or property injection. Don’t get me started about how often people skip proper DI in favor of: getting hold of the context, shamelessly pulling a bean out, then using @DependsOn
to force the dependency. I might write another post about this (if I find a way to do it without giving in to my impulse to shout it in all caps and lots of exclamation marks). Suppose, for argument’s sake, that its use is warrantied because of some hidden dependency on a side effect or something.
The problem, which prompted this post, is that some people think that using @DependsOn
, from a configuration class to some other configuration class, affects the initialization order of the beans declared in them.
Let me demonstrate what’s wrong with this idea, and show you what @DependsOn
really does in that case. Classes foo
and bar
are configuration classes, but I have also exposed their bean faces, by declaring initialization methods in both of them (imports elided for brevity).
@Configuration public class foo { @PostConstruct public void init() { System.out.println("foo.init()"); } @Bean public Long foobean() { System.out.println("foobean"); return System.currentTimeMillis(); } } @Configuration @DependsOn("foo") public class bar { @PostConstruct public void init() { System.out.println("bar.init()"); } @Bean public Long barbean() { System.out.println("barbean"); return System.currentTimeMillis(); } }
The two unit tests that follow register them in two different orders. Scanning a package is the same, except the registering order is arbitrary and does not lead itself to a repeatable test. I’ll tell you how to conduct that test later, if you want to try it.
@Test public void testDependsOnOrderOne() { System.out.println("Running test with bar scanned before foo, and bar having @DependsOn(\"foo\")"); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(bar.class); ctx.register(foo.class); ctx.refresh(); final Long foobean = ctx.getBean("foobean", Long.class); final Long barbean = ctx.getBean("barbean", Long.class); Assert.assertTrue(barbean < foobean); } @Test public void testDependsOnOrderTwo() { System.out.println("Running test with foo scanned before bar, and bar having @DependsOn(\"foo\")"); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(foo.class); ctx.register(bar.class); ctx.refresh(); final Long foobean = ctx.getBean("foobean", Long.class); final Long barbean = ctx.getBean("barbean", Long.class); Assert.assertTrue(barbean > foobean); }
Have a look at what these tests (which both succeed) output.
Running test with bar scanned before foo, and bar having @DependsOn("foo") foo.init() bar.init() barbean foobean Running test with foo scanned before bar, and bar having @DependsOn("foo") foo.init() bar.init() foobean barbean
Notice that @DependsOn
does have an effect. It has the effect it is designed to have: force an initialization order between foo
and bar
. Notice also that it does not have the effect it is believed to have: force any order between beans inside foo
and bar
.
Scanning a package is the exact same thing, only the scanning order is not guaranteed. But it’s easy to overcome this problem if first you establish what order happens to be used in your setup, then fixing the @DependsOn
the other way and assert that it is ignored.
If you’ve been using @DependsOn
between configuration classes, it is time you did some Spring cleaning (a more appropriate pun than the one in the title?).