Saturday, January 14, 2012

Spring Setter Injection and Constructor Injection

In this post i am going to demonstrate you how to set bean property using spring setter injection and constructor. First i will demonstrate Happy scenario and later will be complex. This will force you to think deeply about bean.xml

Consider i have class User containing 3 property name,age and country. Using spring i want to display value of user with all it's property.

User.java
package com.anuj.spring.injection;
/**
 *
 * @author Anuj J Patel
 *
 */
public class User {

    private String name;
    private int age;
    private String country;

    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 String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String toString()
    {
        String output = "Name : " + name + " Age: " + age + " Country:" + country;
        return output;
    }
}

UserApp.java
package com.anuj.spring.injection;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *
 * @author Anuj Patel
 *
 */
public class UserApp {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        System.out.println(user);
    }
}

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

    <bean id="user" class="com.anuj.spring.injection.User"> 
        <property name="name" value="Anuj"></property>
        <property name="age" value="24"></property>
        <property name="country" value="USA"></property>
    </bean>
</beans>


Output -   Name : Anuj Age: 24 Country:USA
Constructor Injection :
public User(String name,int age,String country){
    this.name = name;
    this.age = age;
    this.country = country;
} 

<constructor-arg value="Anuj"/>
<constructor-arg value="24"/>
<constructor-arg value="USA"/>
 Output -   Name : Anuj Age: 24 Country:USA

UnHappy Scenarios :)
If we have 2 constructor as mentioned below and beans xml contains only argument as shown below.
Now which constructor do you think will be invoked? The first one with the int and the String argument, right? But for your surprise it will call the second constructor with both String arguments. Though we know the first argument is of type int and the second argument is of type String, spring interprets both as String arguments

public User(int age,String country){
    this.age = age;
    this.country = country;
}
public User(String name,String country){
    this.name = name;
    this.country = country;
}
 Output - Name : 24 Age: 0 Country:USA

Solution : To avoid this confusion you need to specify the type attribute of the constructor-arg element.
<constructor-arg value="24" type="int"/>
<constructor-arg value="USA" type="java.lang.String"/>
Output -  Name : null Age: 24 Country:USA

Now consider this case. We have the following constructors in the User bean class and beans.xml entries.

public User(String name,int age){
    this.name = name;
    this.age = age;
}
public User(int age,String country){
    this.age = age;
    this.country = country;
}
<constructor-arg value="24" type="int"/>
<constructor-arg value="USA" type="java.lang.String"/>

Now which constructor do you think will be called? The second constructor, right? But again for your surprise the first constructor will be called, this is because the order in which the arguments appear in the bean configuration file will not be considered while invoking the constructor.
Output - Name : USA Age: 24 Country:null

To solve this problem you can use the index attribute to specify the constructor argument index.
Here is the bean configuration file after adding the index attribute.
<constructor-arg value="24" type="int" index="0"/>
<constructor-arg value="USA" type="java.lang.String" index="1"/>

Now Output will be : Name : null Age: 24 Country:USA  ( Now i am damn happy :) )

No comments:

Post a Comment