Tuesday, 29 September 2015

Constructor dependency injection in Spring

In the post Dependency Injection in Spring I have already talked about dependency injection. In this post we'll see one of the type of the dependency injection called Constructor-based DI.

In Constructor-based dependency injection Spring IOC container invokes a class' constructor with arguments that represent the dependencies of that class.

Let's see it with an example, there is one class called Payment service which is dependent on Payment class and also has integer amount and those constructor arguments are injected through a constructor DI.

Payment Service class code

public class PayServiceImpl implements IPayService {
 private IPayment payment;
 private int amount;
 // Constructor
 PayServiceImpl(IPayment payment, int amount){
  this.payment = payment;
  this.amount = amount;
 }
 public void performPayment() {
  payment.executePayment(amount);
 }
}
public interface IPayService {
 void performPayment();
}

Code for the Payment class

public class CashPayment implements IPayment{

 public void executePayment(int amount) {
  System.out.println("Perform Cash Payment - " + amount);
  
 }

}
public interface IPayment {
 void executePayment(int amount);
}

Let's see the XML configuration file appcontext.xml, it has the declaration for the class beans and the constructor dependency injections-

<?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-4.0.xsd">
 
 <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.prog.maven_spring.CashPayment" />

  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.maven_spring.PayServiceImpl">
      <constructor-arg ref="cashPaymentBean" />
      <constructor-arg value="20" />
  </bean>
</beans>

Here note that ref element is used while providing reference to another bean managed by the container. Since cashPaymentBean is declared in the configuration and will be managed by the container, it can be given as value of ref element.

For primitives and Strings value element is used.

If you want to see how it all works use this class -

public class App {
    public static void main( String[] args ){   
     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
        IPayService bean = (IPayService) context.getBean("paymentBean");
        bean.performPayment();
        context.close();
    }
}

How Constructor arguments are resolved

Constructor argument resolution matching occurs using the argument's type. The order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor and there they are matched using type.
So, even if order is not same in bean definition and in the class' constructor arguments will be correctly matched. But arguments may not be resolved correctly if some ambiguity exists as exp. when the arguments are related by inheritance.

Let's say I have 2 classes CashPayment and CreditPayment and they are not related by inheritance.

public class CashPayment{

 public void executePayment() {
  System.out.println("Perform Cash Payment");
  
 }

}
public class CreditPayment{

 public void executePayment() {
  System.out.println("Performing credit payment");

 }

}

XML Config file

<?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-4.0.xsd">
 
 <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.prog.maven_spring.CashPayment" />
  <bean id="creditPaymentBean" class="org.netjs.prog.maven_spring.CreditPayment" />
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.maven_spring.PayServiceImpl">
      <constructor-arg ref="creditPaymentBean" />
      <constructor-arg ref="cashPaymentBean" />    
  </bean>
</beans>

Here I have first provided reference for CreditPayment class and then for CashPayment class. Whereas in the PayServiceImpl class' constructor CashPayment is first and then CreditPayment. Still it will be resolved correctly.

 
public class PayServiceImpl implements IPayService {
 private CashPayment payment1;
 private CreditPayment payment2;
 private int amount;
 
 PayServiceImpl(CashPayment payment1, CreditPayment payment2){
  this.payment1 = payment1;
  this.payment2 = payment2;
 }
 public void performPayment() {
  payment1.executePayment();
  payment2.executePayment();
 }
}

Output

Perform Cash Payment
Performing credit payment

Now if CashPayment and CreditPayment classes are related by inheritance then there will be ambiguity.

public interface IPayment {
 void executePayment();
}
public class CreditPayment implements IPayment {
 public void executePayment() {
  System.out.println("Performing credit payment");
 }

}
public class CashPayment implements IPayment{
 public void executePayment() {
  System.out.println("Perform Cash Payment"); 
 }
}
public class PayServiceImpl implements IPayService {
 private IPayment payment1;
 private IPayment payment2;
 
 
 PayServiceImpl(IPayment payment1, IPayment payment2){
  this.payment1 = payment1;
  this.payment2 = payment2;
 }
 public void performPayment() {
  payment1.executePayment();
  payment2.executePayment();
 }
}
<?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-4.0.xsd">
 
 <!-- defining CashPayment bean -->
  <bean id="cashPaymentBean" class="org.netjs.prog.maven_spring.CashPayment" />
  <bean id="creditPaymentBean" class="org.netjs.prog.maven_spring.CreditPayment" />
  <!-- Defining PayServiceImpl bean and injecting payment bean -->
  <bean id="paymentBean" class="org.netjs.prog.maven_spring.PayServiceImpl">
      <constructor-arg ref="creditPaymentBean" />
      <constructor-arg ref="cashPaymentBean" />    
  </bean>
</beans>
public class App {
    public static void main( String[] args ){   
     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
        IPayService bean = (IPayService) context.getBean("paymentBean");
        bean.performPayment();
        context.close();
    }
}

Output

Performing credit payment
Perform Cash Payment

Here it can be seen that in class PayServiceImpl both CashPayment and CreditPayment have reference through IPayment, so type is same thus whichever class comes first in the bean definition will be configured first.
You can change the reference in the config file and then CashPayment will be called first.

When a primitive type or String is used, we need to explicitly specify the type of the constructor arg using the type attribute.

Let's see an example

public class AmountBean {
 private int amount;
 private String year;
 public AmountBean(int amount, String year) {
  this.amount = amount;
  this.year = year;
 }
 
 public void displayValue(){
  System.out.println("amount - " + amount);
  System.out.println("year - " + year);
 }
}

In the XML config file you can add the bean declaration for the AmountBean class.

<bean id="amountBean" class="org.netjs.prog.maven_spring.AmountBean">
   <constructor-arg value="2015" />    
   <constructor-arg value="200" />
</bean>

In the configuration I have provided value for the string argument year first and then for integer argument amount.

 
public class App {
    public static void main( String[] args ){   
     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("appcontext.xml");
     AmountBean bean = (AmountBean)context.getBean("amountBean");
     bean.displayValue();
        context.close();
    }
}

Output

amount - 2015
year - 200

It can be seen that without specifying the type attribute, container can't resolve it correctly. 2015 is assigned to integer arg and 200 is assigned to String arg.

Using type attribute

type matching can be provided by explicitly specifying the type of the constructor argument using the type attribute.

 
<bean id="amountBean" class="org.netjs.prog.maven_spring.AmountBean">
   <constructor-arg type="java.lang.String" value="2015" />    
   <constructor-arg type="int" value="200" />
</bean>

Using index attribute

Another option is using the index attribute to specify explicitly the index of constructor arguments. Note that index is 0 based.

<bean id="amountBean" class="org.netjs.prog.maven_spring.AmountBean">
   <constructor-arg index="1" value="2015" />    
   <constructor-arg index="0" value="200" />
</bean>

Here I am explicitly saying that first value should be assigned to the second constructor argument and the second value to the first constructor argument.

Reference : Spring framework reference documentation

That's all for this topic Constructor Dependency Injection in Spring. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. What is Dependency Injection in Spring
  2. Setter-based dependency injection in Spring
  3. Spring example program using XML configuration
  4. Spring example program using JavaConfig and Annotations
  5. How to inject prototype scoped bean in singleton bean
  6. Different bean scopes in Spring

You may also like -

>>>Go to Spring tutorial page

No comments:

Post a Comment