Spring Constructor Injection – Resolving Ambiguity

In Spring Constructor Injection we will be injecting values to the property using the Constructor available. Whenever you specify the class attribute for a bean, then you are implicitly asking the Spring IoC container to create the bean instance by invoking its constructor.

Folder Structure:

  1. Create a new Java Project  SpringCoreTutorial” and create a package for our src files com.javainterviewpoint
  2. Add the required libraries to the build path. Java Build Path ->Libraries ->Add External JARs and add the below jars.

    commons-logging-1.2.jar
    spring-beans-4.2.4.RELEASE.jar
    spring-core-4.2.4.RELEASE.jar
    spring-context-4.2.4.RELEASE.jar
    spring-expression-4.2.4.RELEASE.jar

  3. Create the Java classes Student.java and StudentLogic.java under  com.javainterviewpoint folder.
  4. Place our configuration file SpringConfig.xml in the src directory

Student.java

Our Student class will have all the student details such name, age, percentage and its corresponding POJO’s. The getStudentDetails() method will display the student information which is set.

package com.javainterviewpoint;

public class Student
{
    private String name;
    private int age;
    private int percentage;
    
    public Student()
    {
        super();
    }
    public Student(String name, int age, int percentage)
    {
        super();
        this.name = name;
        this.age = age;
        this.percentage = percentage;
    }
    
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getPercentage()
    {
        return percentage;
    }
    public void setPercentage(int percentage)
    {
        this.percentage = percentage;
    }
    
    public void getStudentDetails()
    {
        System.out.println("**Student Details**");
        System.out.println("Student Name       : "+name);
        System.out.println("Student Age        : "+age);
        System.out.println("Student Percentage : "+percentage);
    }
}

SpringConfig.xml

<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="com.javainterviewpoint.Student">
      <constructor-arg value="JavaInterviewPoint" />
      <constructor-arg value="999" />
      <constructor-arg value="100" />
   </bean>
</beans>
  • SpringConfig.xml has the bean definitions, We have set bean id as “student” for our Student class which will act as the reference for calling our Student class.
  • Using the Spring Constructor Dependency Injection <constructor-arg> tag we are setting the values to the properties of the Student class through the constructor.

Spring IoC Container using BeanFactory

BeanFactory is an interface belonging to org.springframework.beans.factory.BeanFactory. We need to instantiate one of the implementations, here we will be instantiating XmlBeanFactory.

package com.javainterviewpoint;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class StudentLogic
{
    public static void main(String args[])
    {
      //Read the configuration file
        Resource resource = new ClassPathResource("SpringConfig.xml");
        //Read all the bean definition
        BeanFactory bf = new XmlBeanFactory(resource);
        //Get the Student class instance
        Student st = (Student)bf.getBean("student");
        //Print the student details
        st.getStudentDetails();
    }
}
  • In our StudentLogic class we will  Read the Configuration file(SpringConfig.xml) through Resource class
  • Bean Factory will take the resource as input to get all the bean instances.
  • Get the Student Class instance by calling the getBean() method over the bean factory.
  • The String passed to getBean() method should be equivalent to the id defined in the SpringConfig.xml
  • Call the getStudentDetails() method to display the values which we injected through the Spring Constructor Injection.

Output : 

Once we run the StudentLogic class, we will be getting the below output

Constructor Injection

 

 

In the above case we have only one parameterized constructor and hence we didn’t have any ambiguity issues. When we are specifying one or more constructor argument for a bean, Spring IoC Container will try to find appropriate constructor and will try to inject. However when more than one constructor is found matching the argument which we passed it will cause ambiguity in Constructor. Lets make some changes to our Student class.

Student.java

package com.javainterviewpoint;

public class Student
{
    private String name;
    private int age;
    private int percentage;
    
    public Student()
    {
        super();
    }
    public Student(String name, String age)
    {
        super();
        System.out.println("Inside Constructor 1");
        this.name = name;
        this.age = Integer.parseInt(age);
    }
    public Student(String name, int age)
    {
        super();
        System.out.println("Inside Constructor 2");
        this.name = name;
        this.age = age;
    }
    
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getPercentage()
    {
        return percentage;
    }
    public void setPercentage(int percentage)
    {
        this.percentage = percentage;
    }
    
    public void getStudentDetails()
    {
        System.out.println("**Student Details**");
        System.out.println("Student Name       : "+name);
        System.out.println("Student Age        : "+age);
        System.out.println("Student Percentage : "+percentage);
    }
}

We will be injecting values like below

 <bean id="student" class="com.javainterviewpoint.Student">
      <constructor-arg value="JavaInterviewPoint" />
      <constructor-arg value="999" />
      <property name="percentage" value="100"/>
   </bean>

Output : 

 Inside Constructor 1
**Student Details**
Student Name : JavaInterviewPoint
Student Age : 999
Student Percentage : 100

Instead of getting into Constructor 2, values got injected through Constructor 1. Spring has considered the value of age as String and injected values through Constructor 1.

Spring Constructor Injection – Resolving Constructor Ambiguity

This ambiguity can be resolved using the type attribute of the <constructor-arg> tag. Lets see Spring Constructor Injection multiple arguments, Now the bean can be written like below.

<bean id="student" class="com.javainterviewpoint.Student">
      <constructor-arg type="java.lang.String" value="JavaInterviewPoint" />
      <constructor-arg type="int" value="999" />
      <property name="percentage" value="100"/>
   </bean>

Now the Constructor 2 will be called correctly as we have added the type attribute and the output will be like below.

Inside Constructor 2
**Student Details**
Student Name : JavaInterviewPoint
Student Age : 999
Student Percentage : 100

This way we can call any parameterized constructor precisely without any ambiguity issues.

Resolving argument order Ambiguity:

If suppose we are having one more parameterized constructor in our Student class.

package com.javainterviewpoint;

public class Student
{
    private String name;
    private int age;
    private int percentage;
    
    public Student()
    {
        super();
    }
    public Student(String name, String age)
    {
        super();
        System.out.println("Inside Constructor 1");
        this.name = name;
        this.age = Integer.parseInt(age);
    }
    public Student(String name, int age)
    {
        super();
        System.out.println("Inside Constructor 2");
        this.name = name;
        this.age = age;
    }
    public Student(int age, String name)
    {
        super();
        System.out.println("Inside Constructor 3");
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public int getAge()
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getPercentage()
    {
        return percentage;
    }
    public void setPercentage(int percentage)
    {
        this.percentage = percentage;
    }
    
    public void getStudentDetails()
    {
        System.out.println("**Student Details**");
        System.out.println("Student Name       : "+name);
        System.out.println("Student Age        : "+age);
        System.out.println("Student Percentage : "+percentage);
    }
}

Our bean is like below

<bean id="student" class="com.javainterviewpoint.Student">
      <constructor-arg type="int" value="999" />
      <constructor-arg type="java.lang.String" value="JavaInterviewPoint" />
      <property name="percentage" value="100"/>
   </bean>

Here the “Constructor2” will be called instead of “Constructor3” this happens because Spring internally scores the constructor for compatibility with the arguments. Now both the Constructor 2 and Constructor 3 gets the same score. Here the order of argument mentioned in the XML is not considered. Spring IoC Container picks up the first matched constructor and hence the Constructor2 will be called.

To avoid this problem, we can indicate the indexes of the arguments explicitly through the index attribute of <constructor-arg>.

<bean id="student" class="com.javainterviewpoint.Student">
      <constructor-arg type="int" index="0" value="999" />
      <constructor-arg type="java.lang.String" index="1" value="JavaInterviewPoint" />
      <property name="percentage" value="100"/>
   </bean>

Now Constructor3 will be called. Specifying index is not a mandatory one, if you are sure that the constructors will not cause any ambiguity.

Leave a Reply

Your email address will not be published. Required fields are marked *