Constructor-based DI is performed by the container calling a constructor with a number of arguments, each of which
represents a dependency. Calling a static
factory method with specific arguments to create a bean is
almost equivalent, and this analysis treats constructor and static
factory method arguments the same
way. The following example shows a class whose dependency can only be injected through the constructor:
public class SimpleMovieLister {
// SimpleMovieLister has a dependency on MovieFinder
private final MovieFinder movieFinder;
// constructor so that the Spring container can inject MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the embedded MovieFinder is omitted...
}
// constructor so that the Spring container can inject MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
// business logic that actually uses the embedded MovieFinder is omitted...
}
Please note that there is nothing special about this class. It is a POJO (plain Java object) that does not depend on specific container interfaces, base classes, or annotations.
Resolving constructor arguments
Constructor argument resolution mapping occurs using the argument type. If there is no potential ambiguity in the constructor arguments of a bean definition, then the order in which the constructor arguments are defined in the bean definition is the order in which those arguments are passed to the corresponding constructor when the bean is created. Consider the following class:
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
package x.y
class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)
Assuming that the classes ThingTwo
and ThingThree
are not related by inheritance, there is
no potential ambiguity. So the following configuration works fine and there is no need to explicitly specify indexes
or types of constructor arguments in the <constructor-arg/>
element.
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
If a reference is provided to another bean, then the type is known, so a mapping can occur (as happened in the
previous example). If a simple type is used, such as <value>true</value>
, Spring cannot
determine the type of the value, and therefore cannot perform type matching without outside help. Consider the
following class:
package examples;
public class ExampleBean {
// Number of years to calculate the final answer
private final int years;
// The answer to the main question of life, the universe and everything
private final String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
package examples
class ExampleBean(
private val years: Int, // Number of years to calculate the final answer
private val ultimateAnswer: String // The answer to the main question of life, the universe and everything
)
In the previous scenario, the container can enable type mapping to simple types if the type of the constructor
argument is explicitly specified using the type
attribute, as shown in the following example:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
You can use the index
attribute to explicitly set the index of the constructor arguments, as shown in
the following example:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
In addition to disambiguating multiple prime values, specifying an index disambiguates if a constructor has two arguments of the same type.
You can also use the constructor parameter name to resolve value conflicts, as shown in the following example:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
Keep in mind that for this to work out of the box, the code must be compiled with the debug flag enabled so that Spring can look up the parameter name in the constructor. If you cannot or do not want to compile your code with the debug flag, then you can use the @ConstructorProperties JDK for explicitly naming your constructor arguments. Then the sample class should look like this:
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
package examples
class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)
GO TO FULL VERSION