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

  1. If you follow the order, no need to specify anything.
  2. If you pass one constructor argument, it will call single parameter constructor.
  3. By default, it will call string argument constructor in case of the overloaded constructor.

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.

  1. commons-logging-1.1.3.jar
  2. spring-beans-5.0.2.RELEASE.jar
  3. spring-context-5.0.2.RELEASE.jar
  4. spring-core-5.0.2.RELEASE.jar
  5. 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.

Student.java
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.

spring.xml
<?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.

type-spring.xml
<?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.

index-spring.xml
<?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.

name-spring.xml
<?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.

Admin.java
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();
	}
}
Output:

You will get the desire result based on the configuration file.

Student name: Atul Rai
Student roll number: 1010
Student course: MCA

Similar Posts

About the Author

Atul Rai
I love sharing my experiments and ideas with everyone by writing articles on the latest technological trends. Read all published posts by Atul Rai.