Consider a bean, call it memberService bean, having two implementations one defined using XML based bean definition files say in a context1.xml file:
<bean name="memberService" class="samples.config.MemberSvcImpl1"/>
and if a different implementation of memberService is needed for test, the way to do it would have been to define the bean with the same name, this way(say in a context2.xml file):
<import resource="context1.xml"/> <bean name="memberService" class="samples.config.MemberSvcImpl2"/>
since the MemberSvcImpl2 is defined after MemberSvcImpl1, the MemberSvcImpl2 would be available for injection in tests.
However with @Configuration style of defining beans, things are not so clear cut. What does it mean to define a bean after another bean, consider the following which tries to mimic the definition in context2.xml file above:
@Configuration @ImportResource("classpath:samples/config/context1.xml") public class Context2Config { @Bean public MemberService memberService() { return new MemberSvcImpl2(); } }
Here the memberService defined in the @Configuration DOES NOT override the memberService bean defined in the xml. Something to be careful about.
The solution is actually simple - if you need to override a previously defined bean(without say the flexibility of autowiring with a different bean name), either use the XML bean configuration for both the bean being overridden and the overriding bean or use the @Configuration. XML bean configuration is the first example in this entry, the one with @Configuration would be something like this:
@Configuration public class Context1JavaConfig { @Bean public MemberService memberService() { return new MemberSvcImpl1(); } } @Configuration @Import(Context1JavaConfig.class) public class Context2JavaConfig { @Bean public MemberService memberService() { return new MemberSvcImpl2(); } }
This would work as expected. I am sure there are alternate ways of getting this to work, but the approach that I have outlined here has worked well for me and I have been sticking to this pattern
Nice post....
ReplyDeleteyes, my search for injecting mocks into spring ctx has finally ended, nicely summarized solution, there's truly a pile on stackoverflow on this topic
ReplyDeleteyes, my search for injecting mocks into spring ctx has finally ended, nicely summarized/clean solution here, there's truly a pile on stackoverflow on this topic
ReplyDeleteThanks... my last comment just disappeared???
ReplyDeleteHow do you make the load-and-override order deterministic when using @Configuration?
I tried the @Configuration approach and found that Context2 memberService was overriden by the Context1 memberService, the opposite of what is intended.
ReplyDeleteHi @dantheperson, it is possible, when I wrote this article I was using an older version of Spring, the behavior may have changed with newer versions, I will check and let you know.
Delete