During lab sessions in our Enterprise Java trainings I usually leave the main track of lab outlines and start trying various cases related with the topic. It is a bit risky for me but it also becomes beneficial for attendees in terms of learning by experimenting with the framework. The risky point is that I may face with undocumented or buggy behaviour with the technology at hand and I have to develop a solution to the problem in a limited time slice or at least bring a reasonable explanation to it.
Recently, I had such a problem during one of my Spring trainings. The problem was related with different autowiring behaviour in Spring Framework. Autowiring is a dependency injection feature which transparently wires up beans with their dependencies if available in application context. It has several modes; no, byType, byName and constructor. According to Spring Framework’s reference documentation in table 5.2;
With this information at hand, lets start playing with the two classes defined as below:
no | (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. |
byName | Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master , and uses it to set the property. |
byType | Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. |
constructor | Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
public class A { private B b; public A() { } public void setB(B b) { this.b = b; } } public class B { }
If you have following bean configurations in your application context, you will have NonUniqueBeanDefinitionException stating that there are more than one bean of the same type as autowire candidate and Spring doesnt know which one to inject.
<bean id="a" class="examples.A" autowire="byType"/> <bean id="b1" class="examples.B" /> <bean id="b2" class="examples.B" />
Everyting as expected according to above documentation. If we change autowire mode to “byName” and add following alias definition, it works because bean with name “b2” is also known as bean “b” and autowire byName matches bean name with the property name.
<alias name="b2" alias="b" />
If we keep alias definition and return back to autowire byType mode, it fails with the same exception again. Still nothing different than what is stated in the documentation.
Lets change autowire mode to “constructor” and also remove alias definition in the xml,we also need to change default no arg constructor so that it will accept an inpur argument in type B. As a result we will again come up with NonUniqueBeanDefinitionException as expected, as autowire constructor is similar to byType but injects via bean constructor.
Now comes the weird point, if we keep autowire mode as constructor but add the alias again we will see no exception! It wires bean name b2 into the bean as in the case with autowire mode byName. We can understand which bean is getting injected by changing alias to something different, eg. “myB” and it fails with the related exception this time. So autowire constructor silently acts like byName but documenation does not say anything about this.
In conclusion, nothing is perfect. Neither is Spring Framework!