Tuesday, November 4, 2014

Spring tutorial 08 - Ambiguity in Constructor Injection in the Spring

When our class contains multiple constructors with same number of arguments, it will always cause the constructor injection argument type ambiguities issue in the Spring framework.
In the our example we will see that how ambiguity occurs in the constructor injection and how to handle this ambiguity issue.

Let's first discuss how ambiguity occurs in constructor injection in the spring.

When we define the constructor injection in the bean file, spring decides implicitly which value declared in the constructor-arg, gets assigned the bean's value in the constructor, by means of three key factors.


  • Matching the quantities or the number of arguments.
  • Matching the type.
  • Matching the order.


Spring try to create bean according to the above defined rules to resolve which constructor is to be chosen to create the bean. There are below possible situation can be arised.


  • Problem resolving the constructor(No ambiguity).
  • No problem resolving but the constructor chosen is leading to in-convertible data( parameter ambiguity).
  • No problem resolving but the constructor chosen is wrong leading to wrong data(parameter ambiguity).


Let's understand with an example.

Let's create a project say "SpringTutorial" now add the required jar support to spring, to add the jar you can follow the Spring Tutorial 02 -My First 'Hello World' Program in Spring blog.

Now create a package with the name of "org.javaIsEasy.springConstructorInjectionAmbiguityExample".

Now create a java file with the name of "Rectangle.java" in this package.

Rectangle.java

*******************************************************************************
package org.javaIsEasy.springConstructorInjectionAmbiguityExample;

public class Rectangle {

 private int length;
 private int width;
 private String message;

 Rectangle(int length,String message, int width)
 {
  this.length=length;
  this.width=width;
  this.message=message;
 
 }

 Rectangle(int width,int length,String message)
 {
  this.length=length;
  this.width=width;
  this.message=message;
 
 }
 public int getLength() {
  return length;
 }

 public int getWidth() {
  return width;
 }
 public String getMessage() {
  return message;
 }


}

*******************************************************************************

Create an other java file in the same package with the name of "Area.java".

In the Area file we will creating object of the Rectangle class and another parameter with the name of 'areaType', we will give the reference of the Rectangle class in this Area class. So let's see how it goes.

Area.java
*******************************************************************************
package org.javaIsEasy.springConstructorInjectionAmbiguityExample;

public class Area {

 private String areaType;
 private Rectangle rectangle;

 public String getAreaType() {
  return areaType;
 }

 public void setAreaType(String areaType) {
  this.areaType = areaType;
 }

 public Rectangle getRectangle() {
  return rectangle;
 }

 public void setRectangle(Rectangle rectangle) {
  this.rectangle = rectangle;
 }


}

*******************************************************************************

Now create another java file with the name of "CallAreaApplication.java" in the same package.
This class has the main method, configuration file will be loaded in this class and let's see how bean is called here.

CallAreaApplication.java
*******************************************************************************

package org.javaIsEasy.springConstructorInjectionAmbiguityExample;

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

public class CallAreaApplication {
public static void main(String[] args) {

 ApplicationContext context = new ClassPathXmlApplicationContext("org/javaIsEasy/springConstructorInjectionAmbiguityExample/Beans.xml");

 Area area=(Area)context.getBean("AreaBean");

 int length=area.getRectangle().getLength();
 int width=area.getRectangle().getWidth();
 String message=area.getRectangle().getMessage();

 String areaType=area.getAreaType();

 System.out.println("Area Name : "+areaType+"\nLength : "+length+"\nWidth : "+width+"\nArea Value : "+length*width+"\nMessage : "+message);
}
}


*******************************************************************************

Now we have to create bean configuration file with the name of "Beans.xml" in the same package. 

Here the Beans.xml file is used to define spring bean configuration. The following code shows how to set a property value through constructor injection.

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

   <bean id="AreaBean" class="org.javaIsEasy.springConstructorInjectionAmbiguityExample.Area">
      <property name="areaType" value="Rectangle"/>
      <property name="rectangle" ref="RectangleBean"/>
   </bean>

 <bean id="RectangleBean" class="org.javaIsEasy.springConstructorInjectionAmbiguityExample.Rectangle">
       <constructor-arg value="Area Calculated"/>
      <constructor-arg  value="6"/>
      <constructor-arg value="4"/> 
      
      
     </bean>
   
</beans>

*******************************************************************************

Structure of the project :





Let's run this program to get the result:

Output:

*******************************************************************************
ERROR: Unsatisfied dependency expressed through constructor argument.......

*******************************************************************************

Now we need to do something resolve this issue.

  • We can resolve this issue by indexing the parameter of the constructor.
Let's apply indexing the "Beans.xml" file.

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

   <bean id="AreaBean" class="org.javaIsEasy.springConstructorInjectionAmbiguityExample.Area">
      <property name="areaType" value="Rectangle"/>
      <property name="rectangle" ref="RectangleBean"/>
   </bean>

 <bean id="RectangleBean" class="org.javaIsEasy.springConstructorInjectionAmbiguityExample.Rectangle">
       <constructor-arg index="1" value="Area Calculated"/>
      <constructor-arg index="0" value="6"/>
      <constructor-arg index="2" value="4"/> 
      
      
     </bean>
   
</beans>

*******************************************************************************
Let's run this program again to get the new result:
Output:

*******************************************************************************
Area Name : Rectangle
Length : 6
Width : 4
Area Value : 24
Message : Area Calculated

*******************************************************************************


We can resolve the issue by indexing the parameter according to the constructor.

  • Now look at the another way to resolve the ambiguity.
We need to modify the "Beans.xml" again, see how it goes.

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

   <bean id="AreaBean" class="org.javaIsEasy.springConstructorInjectionAmbiguityExample.Area">
      <property name="areaType" value="Rectangle"/>
      <property name="rectangle" ref="RectangleBean"/>
   </bean>

 <bean id="RectangleBean" class="org.javaIsEasy.springConstructorInjectionAmbiguityExample.Rectangle">
     
       <constructor-arg name="message" type="java.lang.String" value="Area Calculated"/>
      <constructor-arg name="length" type="int" value="6"/>
      <constructor-arg name="width" type="int" value="4"/>
      
   </bean>
   
</beans>
*******************************************************************************

Let's run this program again to get the new result:
Output:

*******************************************************************************
Area Name : Rectangle
Length : 6
Width : 4
Area Value : 24
Message : Area Calculated

*******************************************************************************


We always define the full qualified the parameter with the name and the type to ignore the ambiguity issue.