Dependency Injection

Dependency Injection (DI) is a design pattern in which an object's dependency is injected by the framework rather than by the object itself. It reduces coupling between multiple objects as it is dynamically injected by the framework. In DI, the framework is completely responsible for reading configuration.

The advantages of DI are as follows:

  • Loosely coupled architecture.
  • Separation of responsibility.
  • Configuration and code are separate.
  • A different implementation can be supplied using configuration without changing the code dependent.
  • Improves testability.
  • DI allows you to replace actual objects with mock objects. This improves testability by writing simple JUnit tests that use mock objects.

Dependency Injection in Spring

In the Spring Framework, DI is used to satisfy the dependencies between objects. It exits in only two types:

  • Constructor Injection: By invoking a constructor containing a number of arguments, constructor-based DI can be accomplished. These arguments are injected at the time of instance instantiation.
  • Setter Injection: Setter-based DI is attained by calling setter methods on your beans. Using setter methods defined in a Spring configuration file, the dependencies are "set" in the objects.

The following figure gives us a better picture:

Dependency Injection in Spring

Let's consider an example where the EmployeeServiceImpl class has an instance field employeeDao of the EmployeeDao type, a constructor with an argument, and a setEmployeeDao method.

In the EmployeeServiceImpl.java class, you'll find the following code:

public class EmployeeServiceImpl implements EmployeeService {

   private EmployeeDao employeeDao;

   public EmployeeServiceImpl(EmployeeDao employeeDao) {
          this.employeeDao = employeeDao;
   }

   public void setEmployeeDao(EmployeeDao employeeDao) {
          this.employeeDao = employeeDao;
   }
}

In the EmployeeDaoImpl.java class, you'll find the following code:

public class EmployeeDaoImpl implements EmployeeDao {

   // ...

}

Here, an instance of EmployeeDao can be provided by the configuration file by either the constructor method or the setter method. Before we understand what these are in more detail, let's understand how generally two objects interact with each other to make an even more meaningful object.

The Has-A relationship

When a class contains another class as instance field; for example, the EmployeeServiceImpl class contains EmployeeDao as its field. This is called a Has-A relationship since we say, "EmployeeServiceImpl has an EmployeeDao". So, without employeeDao, EmployeeServiceImpl cannot perform. The following code illustrates this:

public class EmployeeServiceImpl implements EmployeeService {

    private EmployeeDao employeeDao = null;

}

So, employeeDao is the dependency that needs to be resolved in order to make EmployeeServiceImpl fully functional. The way to create an object of the EmployeeDao type or, in other words, satisfy the dependency of EmployeeServiceImpl in Java is shown here:

public class EmployeeServiceImpl implements EmployeeService {

   private EmployeeDao employeeDao = null;

   public EmployeeServiceImpl() {
          this.employeeDao = new EmployeeDaoImpl();
   }

   public void setEmployeeDao() {
         this.employeeDao = new EmployeeDaoImpl();
   }
}

It is not a very good option as once the EmployeeServiceImpl object is created, you don't have any way to have the object of the employeeDao type swapped with a subclass implementation.

Constructor-based Dependency Injection

Constructor Injection is the process of injecting the dependencies of an object through its constructor argument at the time of instantiating it. In other words, we can say that dependencies are supplied as an object through the object's own constructor. The bean definition can use a constructor with zero or more arguments to initiate the bean, as shown here:

public class EmployeeServiceImpl implements EmployeeService {

   private EmployeeDao employeeDao = null;

   public EmployeeServiceImpl(EmployeeDao employeeDao) {
          this.employeeDao = employeeDao;
   }
}

In the preceding code, the object of the EmployeeDao employeeDao type is injected as a constructor argument to the EmployeeServiceImpl class. We need to configure bean definition in the configuration file that will perform Constructor Injection.

The Spring bean XML configuration tag <constructor-arg> is used for Constructor Injection:

...
   <bean id="employeeService"
   class="org.packt.Spring.chapter2.dependencyinjection.EmployeeServiceImpl">
          <constructor-arg ref="employeeDao" />
   </bean>

   <bean id="employeeDao"
   class="org.packt.Spring.chapter2.dependencyinjection.EmployeeDaoImpl">
   </bean>
...

In the preceding code snippet, there is a Has-A relationship between the classes, which is EmployeeServiceImpl HAS-A EmployeeDao. Here, we inject a user-defined object as the source bean into a target bean using Constructor Injection. Once we have the employeeDao bean to inject it into the target employeeService bean, we need another attribute called ref—its value is the name of the ID attribute of the source bean, which in our case is "employeeDao".

The <constructor-arg> element

The <constructor-arg> subelement of the <bean> element is used for Constructor Injection. The <constructor-arg> element supports four attributes. They are explained in the following table:

Attributes

Description

Occurrence

index

It takes the exact index in the constructor argument list. It is used to avoid ambiguity such as when two arguments are of the same type.

Optional

type

It takes the type of this constructor argument.

Optional

value

It describes the content in a simple string representation, which is converted into the argument type using the PropertyEditors Java beans.

Optional

ref

It refers to another bean in this factory.

Optional

Constructor Injection – injecting simple Java types

Here, we inject simple Java types into a target bean using Constructor Injection.

The Employee class has employeeName as String, employeeAge as int, and married as boolean. The constructor initializes all these three fields.

In the Employee.java class, you'll find the following code:

package org.packt.Spring.chapter2.constructioninjection.simplejavatype;

public class Employee {

   private String employeeName;
   private int employeeAge;
   private boolean married;

   public Employee(String employeeName, int employeeAge, boolean married) {
         this.employeeName = employeeName;
         this.employeeAge = employeeAge;
         this.married = married;

   }
   @Override
   public String toString() {
          return "Employee Name: " + this.employeeName + " , Age:"
                      + this.employeeAge + ", IsMarried: " + married;
   }
}

In the beans.xml file, you'll find the following code:

...
   <bean id="employee"
   class="org.packt.Spring.chapter2.constructioninjection.simplejavatype.Employee">
          <constructor-arg value="Ravi Kant Soni" />
          <constructor-arg value="28" />
          <constructor-arg value="False" />

   </bean>
...

Constructor Injection – resolving ambiguity

In the Spring Framework, whenever we create a Spring bean definition file and provide values to the constructor, Spring decides implicitly and assigns the bean's value in the constructor by means of following key factors:

  • Matching the number of arguments
  • Matching the argument's type
  • Matching the argument's order

Whenever Spring tries to create the bean using Construction Injection by following the aforementioned rules, it tries to resolve the constructor to be chosen while creating Spring bean and hence results in the following situations.

No ambiguity

If no matching constructor is found when Spring tries to create a Spring bean using the preceding rule, it throws the BeanCreationException exception with the message: Could not resolve matching constructor.

Let's understand this scenario in more detail by taking the Employee class from earlier, which has three instance variables and a constructor to set the value of this instance variable.

The Employee class has a constructor in the order of String, int, and boolean to be passed while defining the bean in the definition file.

In the beans.xml file, you'll find the following code:

...
   <bean id="employee"
   class="org.packt.Spring.chapter2.constructioninjection.simplejavatype.Employee">
          <constructor-arg value="Ravi Kant Soni" />
          <constructor-arg value="False" />
           <constructor-arg value="28" />
   </bean>
...

If the orders in which constructor-arg is defined are not matching, then you will get the following error:

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name employee defined in the classpath resource [beans.xml]: Unsatisfied dependency expressed through constructor argument with index 1 of type [int]: Could not convert constructor argument value of type [java.lang.String] to required type [int]: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: "False"
Solution – use index attribute

The solution to this problem is to fix the order. Either we modify the constructor-arg order of the bean definition file or we use the index attribute of constructor-arg as follows:

...
   <bean id="employee"
   class="org.packt.Spring.chapter2.constructioninjection.simplejavatype.Employee">
          <constructor-arg value="Ravi Kant Soni" index="0" />
          <constructor-arg value="False" index="2" />
          <constructor-arg value="28" index="1" />
   </bean>
...

Remember that the index attribute always starts with 0.

Parameter ambiguity

Sometimes, there is no problem in resolving the constructor, but the constructor chosen is leading to inconvertible data. In this case, org.springframework.beans.factory.UnsatisfiedDependencyException is thrown just before the data is converted to the actual type.

Let's understand this scenario in more depth; the Employee class contains two constructor methods and both accept three arguments with different data types.

The following code snippet is also present in Employee.java:

package org.packt.Spring.chapter2.constructioninjection.simplejavatype;

public class Employee {

   private String employeeName;
   private int employeeAge;
   private String employeeId;

   Employee(String employeeName, int employeeAge, String employeeId) {
         this.employeeName = employeeName;
         this.employeeAge = employeeAge;
         this.employeeId = employeeId;
   }

   Employee(String employeeName, String employeeId, int employeeAge) {
         this.employeeName = employeeName;
         this.employeeId = employeeId;
         this.employeeAge = employeeAge;
   }

   @Override
   public String toString() {
          return "Employee Name: " + employeeName + ", Employee Age: "
                     + employeeAge + ", Employee Id: " + employeeId;
   }
}

In the beans.xml file, you'll find the following code:

...
   <bean id="employee"
   class="org.packt.Spring.chapter2.constructioninjection.simplejavatype.Employee">
          <constructor-arg value="Ravi Kant Soni" />
          <constructor-arg value="1065" />
          <constructor-arg value="28" />
   </bean>
...

Spring chooses the wrong constructor to create the bean. The preceding bean definition has been written in the hope that Spring will choose the second constructor as Ravi Kant Soni for employeeName, 1065 for employeeId, and 28 for employeeAge. But the actual output will be:

Employee Name: Ravi Kant Soni, Employee Age: 1065, Employee Id: 28

The preceding result is not what we expected; the first constructor is run instead of the second constructor. In Spring, the argument type 1065 is converted to int, so Spring converts it and takes the first constructor even though you assume it should be a string.

In addition, if Spring can't resolve which constructor to use, it will prompt the following error message:

constructor arguments specified but no matching constructor 
found in bean 'CustomerBean' (hint: specify index and/or 
type arguments for simple parameters to avoid type ambiguities)
Solution – use type attribute

The solution to this problem is to use the type attribute to specify the exact data type for the constructor:

...
   <bean id="employee"
   class="org.packt.Spring.chapter2.constructioninjection.simplejavatype.Employee">

         <constructor-arg value="Ravi Kant Soni" type="java.lang.String"/>
          <constructor-arg value="1065" type="java.lang.String"/>
          <constructor-arg value="28" type="int"/>
   </bean>
...

Now the output will be as expected:

Employee Name: Ravi Kant Soni, Employee Age: 28, Employee Id: 1065

The setter-based Dependency Injection

The setter-based DI is the method of injecting the dependencies of an object using the setter method. In the setter injection, the Spring container uses setXXX() of the Spring bean class to assign a dependent variable to the bean property from the bean configuration file. The setter method is more convenient to inject more dependencies since a large number of constructor arguments makes it awkward.

In the EmployeeServiceImpl.java class, you'll find the following code:

public class EmployeeServiceImpl implements EmployeeService {

   private EmployeeDao employeeDao;

   public void setEmployeeDao(EmployeeDao employeeDao) {
         this.employeeDao = employeeDao;
   }
}

In the EmployeeDaoImpl.java class, you'll find the following code:

public class EmployeeDaoImpl implements EmployeeDao {
   // ...
}

In the preceding code snippet, the EmployeeServiceImpl class defined the setEmployeeDao() method as the setter method where EmployeeDao is the property of this class. This method injects values of the employeeDao bean from the bean configuration file before making the employeeService bean available to the application.

The Spring bean XML configuration tag <property> is used to configure properties. The ref attribute of property elements is used to define the reference of another bean.

In the beans.xml file, you'll find the following code:

...
   <bean id="employeeService"
   class="org.packt.Spring.chapter2.dependencyinjection.EmployeeServiceImpl">
          <property name="employeeDao" ref="employeeDao" />
   </bean>

   <bean id="employeeDao"
   class="org.packt.Spring.chapter2.dependencyinjection.EmployeeDaoImpl">
   </bean>
...

The <property> element

The <property> element invokes the setter method. The bean definition can be describing the zero or more properties to inject before making the bean object available to the application. The <property> element corresponds to JavaBeans' setter methods, which are exposed by bean classes. The <property> element supports the following three attributes:

Attributes

Description

Occurrence

name

It takes the name of Java bean-based property

Optional

value

It describes the content in a simple string representation, which is converted into the argument type using JavaBeans' PropertyEditors

Optional

ref

It refers to a bean

Optional

Setter Injection – injecting a simple Java type

Here, we inject string-based values using the setter method. The Employee class contains the employeeName field with its setter method.

In the Employee.java class, you'll find the following code:

package org.packt.Spring.chapter2.setterinjection;

public class Employee {

   String employeeName;

   public void setEmployeeName(String employeeName) {
         this.employeeName = employeeName;
   }

   @Override
   public String toString() {
          return "Employee Name: " + employeeName;
   }
}

In the beans.xml file, you'll find the following code:

...
   <bean id="employee" class="org.packt.Spring.chapter2.setterinjection.Employee">
         <property name="employeeName" value="Ravi Kant Soni" />
   </bean>
...

In the preceding code snippet, the bean configuration file set the property value.

In the PayrollSystem.java class, you'll find the following code:

package org.packt.Spring.chapter2.setterinjection;

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

public class PayrollSystem {

   public static void main(String[] args) {
         ApplicationContext context = new ClassPathXmlApplicationContext(
                      "beans.xml");
         Employee employee = (Employee) context.getBean("employee");
         System.out.println(employee);
   }
}

The output after running the PayrollSystem class will be as follows:

INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@1ba94d: startup date [Sun Jan 25 10:11:36 IST 2015]; root of context hierarchy
Jan 25, 2015 10:11:36 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [beans.xml]
Employee Name: Ravi Kant Soni

Setter Injection – injecting collections

In the Spring IoC container, beans can also access collections of objects. Spring allows you to inject a collection of objects in a bean using Java's collection framework. Setter Injection can be used to inject collection values in the Spring Framework. If we have a dependent object in the collection, we can inject this information using the ref element inside the list, set, or map. Let's discuss them in more detail:

  • <list>: This element describes a java.util.List type. A list can contain multiple bean, ref, value, null, another list, set, and map elements. The necessary conversion is automatically performed by BeanFactory.
  • <set>: This element describes a java.util.Set type. A set can contain multiple bean, ref, value, null, another set, list, and map elements.
  • <map>: This element describes a java.util.Map type. A map can contain zero or more <entry> elements, which describes a key and value.

The Employee class is a class with an injecting collection.

In the Employee.java class, you'll find the following code:

package org.packt.Spring.chapter2.setterinjection;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class Employee {

   private List<Object> lists;
   private Set<Object> sets;
   private Map<Object, Object> maps;

   public void setLists(List<Object> lists) {
          this.lists = lists;
   }

   public void setSets(Set<Object> sets) {
          this.sets = sets;
   }

   public void setMaps(Map<Object, Object> maps) {
          this.maps = maps;
   }
}

The bean configuration file is the one that injects each and every property of the Employee class.

In the beans.xml file, you'll find the following code:

...
<bean id="employee" class="org.packt.Spring.chapter2.setterinjection.Employee">
  <property name="lists">
    <list>
      <value>Ravi Kant Soni</value>
      <value>Shashi Kant Soni</value>
      <value>Shree Kant Soni</value>
    </list>
  </property>
  <property name="sets">
    <set>
      <value>Namrata Soni</value>
      <value>Rishi Raj Soni</value>
    </set>
  </property>
  <property name="maps">
    <map>
      <entry key="Key 1" value="Sasaram"/>
      <entry key="Key 2" value="Bihar"/>
    </map>
  </property>
</bean>
...

In the preceding code snippet, we injected values of all three setter methods of the Employee class. The List and Set instances are injected with the <list> and <set> tags. For the map property of the Employee class, we injected a Map instance using the <map> tag. Each entry of the <map> tag is specified with the <entry> tag that contains a key-value pair of the Map instance.

Injecting inner beans

Similar to the concept of inner classes in Java, it is also possible to define a bean inside another bean; for example, in an Automated Teller Machine (ATM) system, we can have a printer bean as an inner bean of the ATM class.

The following are the characteristics of inner beans in Spring:

  • A bean can optionally be declared as an inner bean when it doesn't need to be shared with other beans.
  • An inner bean is defined within the context of its enclosing bean.
  • Typically, the inner bean does not have an ID associated with it because the inner bean will not be shared outside of its enclosing bean. We can associate an ID; however, the value of this ID attribute is ignored by Spring.
  • The inner class is independent of the inner bean. Any class can be defined as an inner bean; for instance, a Printer class is not an inner class, but a printer bean is defined as an inner bean.
  • The scope of an inner bean is always a prototype.

The limitations of using inner beans are as follows:

  • It cannot be reused or shared with other beans
  • In practice, it affects the readability of the configuration file

An ATM class has a Printer class. We'll declare the printer bean as an inner bean (inside the enclosing ATM bean) since the Printer class is not referenced anywhere outside the ATM class. The printBalance() method of ATM delegates the call to the printBalance() method of the printer. The printer bean will be declared as an inner bean and will then be injected into the ATM bean using Setter Injection.

The ATM class delegates the call to print the balance to the Printer class.

The following code snippet can also be found in ATM.java:

package org.packt.Spring.chapter2.setterinjection;

public class ATM {

   private Printer printer;

   public Printer getPrinter() {
         return printer;
   }

   public void setPrinter(Printer printer) {
          this.printer = printer;
   }

   public void printBalance(String accountNumber) {
          getPrinter().printBalance(accountNumber);
   }
}

In the preceding code snippet, the ATM class has a Printer class as property and the setter, getPrinter(), and printBalance() methods.

In the Printer.java class, you'll find the following code:

package org.packt.Spring.chapter2.setterinjection;

public class Printer {

   private String message;

   public void setMessage(String message) {
          this.message = message;
   }

   public void printBalance(String accountNumber) {

          System.out.println(message + accountNumber);
   }
}

In the preceding code snippet, the Printer class has the printBalance() method. It has a message property, and a setter method sets the message value from the bean configuration file.

In the beans.xml file, you'll find the following code:

...
   <bean id="atmBean" class="org.packt.Spring.chapter2.setterinjection.ATM">

          <property name="printer">
              <bean class="org.packt.Spring.chapter2.setterinjection.Printer">
                        <property name="message"
                           value="The balance information is printed by Printer for the account number"></property>
               </bean>
         </property>

   </bean>
...

Here, we declare atmBean. We declare the printer bean as an inner bean by declaring inside the enclosing atmBean. The id attribute cannot be used outside the context of atmBean and hence hasn't been provided to the printer bean.

Injecting null and empty string values in Spring

We come across two cases while injecting null and empty string values.

Case 1 – injecting an empty string

We can pass an empty string as a value, as shown in the following code, which is like setEmployeeName("") in the Java code:

...
<bean id="employee" class="org.packt.Spring.chapter2.setterinjection.Employee">
         <property name="employeeName" value="""></property>
</bean>
...

Case 2 – injecting a null value

We can pass a null value, as shown in the following code, which is like setEmployeeName(null) in the Java code:

...
   <bean id="employee" class="org.packt.Spring.chapter2.setterinjection.Employee">

         <property name="employeeName">
                <null />
         </property>
   </bean>
...
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.51.241