Spring Constructor-based Dependency Injection Example
On this page, we will learn constructor-based dependency injection in Spring framework. Constructor-based dependency injection 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.
To inject the value for the parameterized constructor, we have a <constructor-arg />
subelement of bean.
Key Points
- If you follow the order, no need to specify anything.
- If you pass one constructor argument, it will call single parameter constructor.
- By default, it will call string argument constructor in case of the overloaded constructor.
You may be interested in Spring Setter-based Dependency Injection Example
Dependencies Required
To develop Spring constructor-based DI, we hardly need the four to five JARs of Spring framework. These JARs file name is given below.
- commons-logging-1.1.3.jar
- spring-beans-5.0.2.RELEASE.jar
- spring-context-5.0.2.RELEASE.jar
- spring-core-5.0.2.RELEASE.jar
- spring-expression-5.0.2.RELEASE.jar
Constructor Argument Resolution
Let’s see the below Student
class which has a single string argument constructor. There is nothing special about this class, it is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
package org.websparrow.beans;
public class Student {
private String name;
// String argument constructor
public Student(String name) {
this.name = name;
}
// Business logic that uses the injected value
public void studentReport() {
System.out.println("Student Name: " + name);
}
}
For the above class, if there is no potential ambiguity exists, then the following configuration works fine, and you do not need to specify the constructor argument indexes and/or types explicitly in the <constructor-arg />
element.
<beans>
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="Atul Rai" />
</bean>
</beans>
Constructor Argument Type Matching
Suppose, you have added another field int rollNumber in Student
class and another integer argument constructor. Then you must face the ambiguity problem for injecting the value to the constructor.
package org.websparrow.beans;
public class Student {
private String name;
private int rollNumber;
// String argument constructor
public Student(String name) {
this.name = name;
}
// Integer argument constructor
public Student(int rollNumber) {
this.rollNumber = rollNumber;
}
// Business logic that uses the injected value
public void studentReport() {
// System.out.println("Student Name: " + name);
System.out.println("Student roll number: " + rollNumber);
}
}
For the above class, if your configuration like this and you are passing an integer value to inject the value for integer argument constructor.
<beans>
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="1010" />
</bean>
</beans>
It will not inject the value for integer argument constructor. You have to remember the Key Point number 3.
By default, it will call string argument constructor in case of the overloaded constructor.
To resolve this type of ambiguity, you can use type
attribute of <constructor-arg />
element. And the below configuration works fine. It will inject the value for integer argument constructor.
<beans>
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="1010" type="int" />
</bean>
</beans>
Constructor Argument Index
Now, come to the next scenario, if the Student
class have more than one argument constructor, then type
attribute will not work. Let’s add another field in String course in Student
class.
package org.websparrow.beans;
public class Student {
private String name;
private int rollNumber;
private String course;
// String argument constructor
public Student(String name) {
this.name = name;
}
// Integer argument constructor
public Student(int rollNumber) {
this.rollNumber = rollNumber;
}
// More than one argument constructor
public Student(String name, int rollNumber, String course) {
this.name = name;
this.rollNumber = rollNumber;
this.course = course;
}
// Business logic that uses the injected value
public void studentReport() {
System.out.println("Student name: " + name);
System.out.println("Student roll number: " + rollNumber);
System.out.println("Student course: " + course);
}
}
To inject the value for more than one argument constructor, you just need to pass the same number of <constructor-arg />
element.
Remember: If you follow the order, no need to specify anything.
To resolve this type of ambiguity, you can use index
attribute of <constructor-arg />
element. And the below configuration works fine. It will inject the value for more than one argument constructor.
<beans>
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="1010" index="1" />
<constructor-arg value="Atul Rai" index="0" />
<constructor-arg value="MCA" index="2" />
</bean>
</beans>
In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has more than one arguments of the same type.
Note that the index is 0 based.
Constructor Argument Name
You can also use the constructor argument name for value disambiguation. To do that, you can use the name
attribute of <constructor-arg />
element. See the configuration.
<beans>
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="MCA" name="course" />
<constructor-arg value="1010" name="rollNumber" />
<constructor-arg value="Atul Rai" name="name" />
</bean>
</beans>
Complete Example
Here is our final Student
class which contains the three constructor having different arguments.
package org.websparrow.beans;
public class Student {
private String name;
private int rollNumber;
private String course;
// String argument constructor
public Student(String name) {
this.name = name;
}
// Integer argument constructor
public Student(int rollNumber) {
this.rollNumber = rollNumber;
}
// More than one argument constructor
public Student(String name, int rollNumber, String course) {
this.name = name;
this.rollNumber = rollNumber;
this.course = course;
}
// Business logic that uses the injected value
public void studentReport() {
System.out.println("Student name: " + name);
System.out.println("Student roll number: " + rollNumber);
System.out.println("Student course: " + course);
}
}
And finally, we have 4 configurations file.
1: spring.xml this will execute the one argument string constructor.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="Atul Rai" />
</bean>
</beans>
2: type-spring.xml this will execute the one argument integer constructor.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="1010" type="int" />
</bean>
</beans>
3: index-spring.xml this will execute the 3 argument constructor.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="MCA" index="2" />
<constructor-arg value="1010" index="1" />
<constructor-arg value="Atul Rai" index="0" />
</bean>
</beans>
4: name-spring.xml this will also execute the 3 argument constructor.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="org.websparrow.beans.Student">
<constructor-arg value="MCA" name="course" />
<constructor-arg value="1010" name="rollNumber" />
<constructor-arg value="Atul Rai" name="name" />
</bean>
</beans>
Now create an Admin
class and load the configurations file one by one into the container and run it.
package org.websparrow.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.websparrow.beans.Student;
public class Admin {
public static void main(String[] args) {
//ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//ApplicationContext context = new ClassPathXmlApplicationContext("type-spring.xml");
//ApplicationContext context = new ClassPathXmlApplicationContext("index-spring.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("name-spring.xml");
Student stu = (Student) context.getBean("student");
stu.studentReport();
}
}
You will get the desire result based on the configuration file.
Student name: Atul Rai
Student roll number: 1010
Student course: MCA
Download Source Code: spring-constructor-based-dependency-injection-example