2020-02-14

Spring 5 - Constructor-based dependency injection

Constructor-based dependency injection 


Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static factory method similarly. The following example shows a class that can only be dependencyinjected with constructor injection. Notice that there is nothing special about this class, it is a POJO that has no dependencies on container specific interfaces, base classes or annotations.



public class SimpleMovieLister { 

// the SimpleMovieLister has a dependency on a MovieFinder 
 private MovieFinder movieFinder; 

 // a constructor so that the Spring container can inject a MovieFinder
 public SimpleMovieLister(MovieFinder movieFinder) {
   this.movieFinder = movieFinder; 
  } 
 // business logic that actually uses the injected MovieFinder is omitted... 


Constructor argument resolution 


Constructor argument resolution matching occurs using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class:

package x.y;

public class Foo {

public Foo(Bar bar, Baz baz) { // ... } 


No potential ambiguity exists, assuming that Bar and Baz classes are not related by inheritance. Thus the following configuration works fine, and you do not need to specify the constructor argument indexes and/or types explicitly in the element.

<beans>
 <bean id="foo" class="x.y.Foo">
 <constructor-arg ref="bar"/>
 <constructor-arg ref="baz"/>
 </bean>
 <bean id="bar" class="x.y.Bar"/>
 <bean id="baz" class="x.y.Baz"/>
</beans>

When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as true, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:

package examples; 
public class ExampleBean { 
 // Number of years to calculate the Ultimate Answer 
 private int years; 
 // The Answer to Life, the Universe, and Everything 
 private String ultimateAnswer; 
 public ExampleBean(int years, String ultimateAnswer) {
 this.years = years; this.ultimateAnswer = ultimateAnswer; 
 }
 } 
In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument using the type attribute. For example:

<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
 </bean>

Use the index attribute to specify explicitly the index of constructor arguments. For example:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
 <constructor-arg index="1" value="42"/>
</bean>

In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has two arguments of the same type. Note that the index is 0 based.

You can also use the constructor parameter name for value disambiguation:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
 </bean>

Keep in mind that to make this work out of the box your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you can’t compile your code with debug flag (or don’t want to) you can use @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:

package examples; 
public class ExampleBean { 
 // Fields omitted 
 @ConstructorProperties({"years", "ultimateAnswer"}) 
 public ExampleBean(int years, String ultimateAnswer) { 
 this.years = years;
 this.ultimateAnswer = ultimateAnswer; 
 } 










No comments:

Post a Comment