10 - Spring Dependency Injection

10.1 Overview of Spring Dependency Injection

Dependency Injection is at the heart of the Spring framework. The basic concept of the Dependency Injection is that framework provides the dependencies of class itself.

We need not create the objects instead we just define how they should be created and IoC container will create the objects for us.

For example if we have two classes ‘ClassA’ and ‘ClassB’. ClassA is dependent on ClassB then container will inject the instance of ClassB in an instance of ClassA.

This feature is very useful specially in a large and complex applications where it is very important to have the classes loosely coupled. In other words, Dependency Injection allows us to remove the hard-coded dependencies and make our application loosely coupled, extendable and maintainable.

In traditional java we can have a instance of an object in other using two ways –

a) Constructor

public Hostel{
    private Room room;
    public Hostel(Room room)
    {
      this.room=room;
    }
}

b) Setters

public Hostel{
       private Room room;
       public  void setRoom(Room room){
                this.room=room;
       }
}

Similarly Spring framework provides Constructor based and Setter based Dependency injection.

We can use both, Constructor-based and Setter-based DI together but it is a advisable to use constructor arguments for mandatory dependencies and setters for optional dependencies.

10.2 Constructor Based Dependency Injection

By default when Spring container loads the beans, it instantiates the beans using default constructor (zero argument constructor) but can have define a constructor argument in bean definition in case we want to instantiate using a argument constructors

For example if we have below Hostel class

public class Hostel {
            private int rooms;
            public Hostel()
            {
                        System.out.println("Default Constructor");
            }
            public Hostel(int rooms )
            {
                        System.out.println("One Arg Constructor");
                        this.rooms=rooms;
            }
}

Defining Hostel bean using <bean id="hostel" class="Hostel" /> will instantiate Hostel class using a default constructor where as using below will instantiate using single argument constructor

<bean id="hostel" class="Hostel">
                <constructor-arg value="30"/>
</bean>

10.2.1 Constructor Arguments Resolution

We need to define one constructor-arg for one constructor argument and constructor arguments are resolved by orders. For example a class has a two argument constructor , then we will define two constructor-arg tags and first tag will be mapped to first argument of constructor and so on..

There can be multiple constructors like

  1. Class has two constructors and both are having two arguments but of different types.
  2. Class has two constructors and both are having different number of arguments.

Thumb Rules:

a)There must be a constructor having arguments equal to number of constructor-arg tags.

b) If Class has two constructors and both are having two arguments but of different types then constructor having same argument types (in constructor and in constructor-arg ) will be used

c) if #b does not work, type conversion happens (if possible)

d) if there is no constructor that matches constructor-arg then exception will be thrown

Lets define below confifuration

<bean id="person" class="Person">
         <constructor-arg value ="40" />
         <constructor-arg value ="1400" />
</bean>

Case 1: Constructor (String ,String) of Person class from below two constructors will be invoked because we have matched type

public Person(int age, int salary)
{         
}
public Person(String age, String salary)
{
}

Case 2: Constructor (int ,int) of Person class from below two constructors will be invoked because String,String constructor is not present and values can be typed to int

public Person(int age, int salary)
{               
}
public Person(int age, String salary)
{
}

Case 3: Constructor (int ,String) of Person class will be invoked if there is only one below constructor available

public Person(int age, String salary)
{
}

10.2.2 Injecting beans using Constructor Dependency Injection

In above example we have passed simple types like int, string in constructor arg tag but in real scenarios , constructors often expects object types.

For such scenarios , we can use ref attribute of constructor-arg tag instead of value attribute. Value of ref attribute will be the name of bean defined in beans configuration

For example

<bean id="hostel" class="Hostel">
                <constructor-arg ref="room"/>
</bean>
<bean id="room" class="Room"/>

Configuration will work for below Hostel class

public Hostel{
    private Room room;
    public Hostel(Room room){
           this.room=room;
    }
}

10.3 Setter Based Dependency Injection

Setter-based Dependency Injection is accomplished by the container calling setter methods on your beans after invoking a no-argument. We can use <property> tag inside a bean to set the values.

For example if we have below Hostel class

public class Hostel{
      private int rooms;
      public void setRooms(int rooms){
           this.rooms=rooms;
      }             
}

Below configuration will instantiate Hostel class using a default constructor and then sets the value of rooms to 30

<bean id="hostel" class="Hostel">
      <property name=”rooms” value="30"/>
</bean>

We can have multiple property tag one for each property. Similar to constructor-arg tag, we can use ref attribute instead of value for objects

For example

<bean id="hostel" class="Hostel">
     <property name=”room”  ref="room"/>
</bean>
<bean id="room" class="Room"/>

Configuration will work for below Hostel class

public Hostel{
   private Room room;
   public void setRoom(Room room){
         this.room=room;
   }
}

10.3.1 Setting value as null

We can use <null/> tag to set the value of property as null in beans configuration like

     <property name="name" ><null/> </property>

10.3.2 Setting blank value

We can use <value/> tag to set the value of property as blank in beans configuration like

     <property name="name" ><value/> </property>

10.3.3 Setting Compounded property names

We can use period (.) in the name attribute of a property tag if the property is compounded.

For example if Foo class has an instance of Bar class and Bar class has a property name then below configuration can be used.

<bean id="foo" class="Foo">
      <property name=”bar.name” value="Test value "/>
</bean>

10.4 Constructor and Setter Based Dependency Injection together

We can use both, Constructor-based and Setter-based DI together but it is a advisable to use constructor arguments for mandatory dependencies and setters for optional dependencies.

In this strategy, object is created using the arguments constructor and then setters are called. For example in below example , object will be created using one argument constructor and then setRooms method will be invoked

public class Hostel{
       private int rooms;
       private int  floors;
       public void setRooms(int rooms){
            this.rooms=rooms;
       }             
       public Hostel(int floors){
            this.floors=floors;
       }
}
<bean id="hostel" class="Hostel">
       <property name=”rooms” value="30"/>
       <constructor-arg value="10"/>
</bean>

10.5 Examples

10.5.1 Write an example to demonstrate constructor based dependency injection

Solution :

  1. Create a Student class as below
public class Student {
       private String name ;
       public String getName() {
              return name;
}
      public void setName(String name) {
              this.name = name;
       }     
}
  1. Create Room class as below
public class Room
{
   private int roomNumber;
   private Student allotedTo;
   public Room(int roomNumber, Student allotedTo) {
          super();
          this.roomNumber = roomNumber;
          this.allotedTo = allotedTo;
   }
   @Override
   public String toString() {
          return "Room [roomNumber=" + roomNumber + ", allotedTo=" + allotedTo.getName()
                      + "]";
   }
}
  1. Define below beans entry in 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="room" class="Room">
    <constructor-arg value="101"/>
    <constructor-arg ref="StudentA"/>
  </bean>
  <bean id="StudentA"  class="Student" >
          <property name="name" value="joe bloggs"/>
  </bean>
</beans>

d.  Create TestDependencyInjection class to test the example.

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDependencyInjection {
public static void main(String[] args) {
       ApplicationContext context =
       new ClassPathXmlApplicationContext("beans.xml");
       Room room = (Room)context.getBean("room");
       System.out.println(room);
       }
}

e.  Run the TestDependencyInjection. You will see the below results

10.5.2 Update the above example to demonstrate setter based dependency injection

Solution :

a. Update Room class as below

public class Room
{
       private int roomNumber;
       private Student allotedTo;
               public int getRoomNumber() {
              return roomNumber;
       }
       public void setRoomNumber(int roomNumber) {
              this.roomNumber = roomNumber;
       }
       public Student getAllotedTo() {
              return allotedTo;
       }
       public void setAllotedTo(Student allotedTo) {
              this.allotedTo = allotedTo;
       }
       @Override
       public String toString() {
        return "Room [roomNumber=" + roomNumber + ", allotedTo=" + allotedTo.getName()
                           + "]";
       }
 }

Update beans entry in 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="room" class="Room">
    <property name="roomNumber" value ="101"/>
    <property name="allotedTo" ref ="StudentA"/>
  </bean>
  <bean id="StudentA"  class="Student" >
          <property name="name" value="joe bloggs"/>
  </bean>
</beans>
  1. Run the TestDependencyInjection. You will see the below results

10.5.3 Update the example created in section 10.5.1 to demonstrate both constructor and setter based dependency injection together

Solution :

  1. Update Room class as below
public class Room
{
       private int roomNumber;
       private Student allotedTo;
       public int getRoomNumber()
       {
              return roomNumber;
       }      
       public void setRoomNumber(int roomNumber)
       {
              this.roomNumber = roomNumber;
       }      
       public Room(Student allotedTo)
       {
              this.allotedTo=allotedTo;
                          
       }      
       @Override
       public String toString() {
              return "Room [roomNumber=" + roomNumber + ", allotedTo=" + allotedTo.getName()
                          + "]";
       }     
}

Update beans entry in 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="room" class="Room">
       <constructor-arg ref ="StudentA"/>
       <property name="roomNumber" value="101"/>
    </bean>
    <bean id="StudentA"  class="Student" >
              <property name="name" value="joe bloggs"/>
    </bean>
</beans>

Run the TestDependencyInjection. You will see the below results

10.6 XML Configuration using p-namespace

Our bean class can have several properties and sometimes it becomes very tedious to define several property tags (one for each property) . To address this issue , Spring has provided an alternate approach of p-namespace.

For example if my bean definition is

<?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="room" class="Room">
       <property name="roomNumber" value ="101"/>
       <property name="allotedTo" ref ="StudentA"/>
    </bean>
    <bean id="StudentA"  class="Student" >
       <property name="name" value="joe bloggs"/>
    </bean>
</beans>

We can alternatively write it as

<?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"
     xmlns:p="http://www.springframework.org/schema/p"
    >
   <bean id="room" class="Room"
      p:roomNumber="101"
      p:allotedTo-ref="StudentA"/>
   </bean>
   <bean id="StudentA"  class="Student" >
      <property name="name" value="joe bloggs"/>   
   </bean>
</beans>

The -ref part indicates that this is not a simple type but rather a reference to another bean. Also you need to bound ‘p’ tag in beans namespace as highlighted .

10.7 Aliasing Beans

We can introduce an alias of a bean defined in same beans configuration file. Once done the bean can be referred using alias as well.

     <alias name="fromName" alias="toName"/>

With the above snippet bean defined with name “fromName” can be used using “toName” name.

10.8 Bean Instantiation using static factory method

There are scenarios where classes are singleton which means constructor in such classes are marked as private and a static method is exposed get the instance. In such scenarios , standard bean configurations will not work .

“bean” tag provides an attribute factory-method to specify the static method which needs to be invoked to get the instance.

For example if class looks like –

public class Person {
   private static Person person = new Person();
   private Person()
   {
   }  
   private String name ;
   public String getName() {
          return name;
   }
   public void setName(String name) {
          this.name = name;
   }     
   public static Person getInstance()
   {
          return person;
   }
} 

then bean should be defined as

<?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"
     xmlns:p="http://www.springframework.org/schema/p">
  <bean id="person"  class="Person" factory-method="getInstance">
        <property name="name" value="joe bloggs"/>
 </bean>
</beans>

10.9 Bean Instantiation using instance factory method

Situation where a instance of bean should be created using instance method of another bean defined in same beans configuration file can be achieved using factory-bean and factory-method attributes of a bean tag.

For example Student class object needs to be instantiated from School class

public class School {
   private String name ;
   public String getName() {
          return name;
   }
   public void setName(String name) {
          this.name = name;
   }  
   public Student getStudent()
   {
          return new Student();
   }
}
public class Student {
}

then bean should be defined as

<?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"
     xmlns:p="http://www.springframework.org/schema/p"
    >
  <bean id="school"  class="School" >
          <property name="name" value ="School name" />                      
  </bean>
  <bean id="student" factory-bean="school" factory-method="getStudent" />
</beans>

10.10 Beans Creation Dependency Order

If our bean has a dependency that some other beans must be initialized before our bean is created then we can use depends-on attribute of bean tag like below

<bean id="person"  class="Person" depends-on "OtherBean">
  <property name="name" value="joe bloggs"/>
</bean>

Like us on Facebook