Constructor injection type ambiguities in Spring

In Spring framework, when your class contains multiple constructors with same number of arguments, it will always cause the constructor injection argument type ambiguities issue.


Let us consider the below Employee class with different types of arguments. One with String,String as arguments and other with float,String as arguments.


We can inject following types of values by using constructor injection.

Employee.java

package com.jwt.spring;

public class Employee {
	private String name;
	private String designation;
	private float salary;

	public Employee(String name, String designation) {
		this.name = name;
		this.designation = designation;
	}

	public Employee(float salary, String name) {
		this.salary = salary;
		this.name = name;

	}

	public void displayResult() {
		System.out.println("Name: " + name);
		System.out.println("Designation: " + designation);
		System.out.println("Salary: " + salary);
	}

}

beans.xml

Below is the code snippet to use the Set in the spring bean configuration file.

<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-3.0.xsd">

	<bean id="employee" class="com.jwt.spring.Employee">
		<constructor-arg value="10000" />
		<constructor-arg value="Mukesh" />
	</bean>
</beans>

In the above configuration, we want to pass 10000 to salary and Mukesh to name i.e. second constructor (float,String argument) need to be called.

Now create a Test class to check the result.

package com.jwt.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"beans.xml");

		Employee employee = (Employee) context.getBean("employee");
		employee.displayResult();

	}

}


Output :

Name: 10000
Designation: Mukesh
Salary: 0.0

If you observe the above output we didn't get expected result. ie. second constructor (float,String) argument was not called, instead first constructor (String,String) argument was called. This is the Constructor injection type ambiguity.

Spring container by default converts every passing value to String value. i.e. In our example 10000 converted to String. That's why second constructor with (String,String) argument was called.


Solution:

We can resolve this problem by using type attribute of <constructor-arg> tag. Always specify the exact datatype for constructor-arg value using type attribute in bean configuration file.

Update the bean.xml file as given below.

<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-3.0.xsd">

	<bean id="employee" class="com.jwt.spring.Employee">
		<constructor-arg type="float" value="10000" />
		<constructor-arg type="java.lang.String" value="Mukesh" />
	</bean>
</beans>

Output :

Name: Mukesh
Designation: null
Salary: 10000.0

Now the ambiguity is solved, but consider one more scenario by adding one more constructor in Employee class.Add one more constructor with String, float as arguments in Employee.java.Updated code is given below.


package com.jwt.spring;

public class Employee {
	private String name;
	private String designation;
	private float salary;

	public Employee(String name, String designation) {
		this.name = name;
		this.designation = designation;
		System.out.println("1st constructor called");
	}

	public Employee(String designation, float salary) {
		this.designation = designation;
		this.salary = salary;
		System.out.println("2nd constructor called");
	}

	public Employee(float salary, String name) {
		this.salary = salary;
		this.name = name;
		System.out.println("3rd constructor called");
	}

	public void displayResult() {
		System.out.println("Name: " + name);
		System.out.println("Designation: " + designation);
		System.out.println("Salary: " + salary);
	}

}

Again Run the application but surprise you will get following output.


Output :

Name: null
Designation: Mukesh
Salary: 10000.0

If you observe we didn't get expected output. ie. third constructor with (float,String) argument was not called, instead second constructor with (String,float) argument was called. This is again ambiguity in Constructor injection type.



Solution:

We can resolve this problem by using index attribute of <constructor-arg> tag. So while using constructor injection always specify the exact datatype for constructor-arg value using type attribute and index attribute in beans.xml.

Update the bean.xml file as given below.

<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-3.0.xsd">

	<bean id="employee" class="com.jwt.spring.Employee">
		<constructor-arg type="float" index="0" value="10000" />
		<constructor-arg type="java.lang.String" index="1"
			value="Mukesh" />
	</bean>
</beans>

Output :

3rd constructor called
Name: Mukesh
Designation: null
Salary: 10000.0


Conclusion :

It’s always a good practice to explicitly declare the data type and index for each constructor argument, to avoid constructor injection type ambiguities issue.






comments powered by Disqus