Constructor-based DI

Constructor-based dependency is generally used where you want to pass mandatory dependencies before the object is instantiated. It's provided by a container through a constructor with different arguments, and each represents dependency.

When a container starts, it checks wheather any constructor-based DI is defined for <bean>. It will create the dependency objects first, and then pass them to the current object's constructor. We will understand this by taking the classic example of using logging. It is good practice to put the log statement at various places in code to trace the flow of execution.

Let's say you have an EmployeeService class where you need to put a log in each of its methods. To achieve separation of concern, you put the log functionality in a separated class called Logger. To make sure the EmployeeService and Logger are independent and loosely coupled, you need to inject the Logger object into the EmployeeService object. Let's see how to achieve this by constructor-based injection:

public class EmployeeService {
private Logger log;
//Constructor
public EmployeeService(Logger log) {
this.log = log;
}

//Service method.
public void showEmployeeName() {
log.info("showEmployeeName method is called ....");
log.debug("This is Debuggin point");
log.error("Some Exception occured here ...");
}

}

public class Logger {
public void info(String msg){
System.out.println("Logger INFO: "+msg);
}
public void debug(String msg){
System.out.println("Logger DEBUG: "+msg);
}
public void error(String msg){
System.out.println("Logger ERROR: "+msg);
}
}


public class DIWithConstructorCheck {

public static void main(String[] args) {

ApplicationContext springContext = new ClassPathXmlApplicationContext("application-context.xml");
EmployeeService employeeService = (EmployeeService) springContext.getBean("employeeService");
employeeService.showEmployeeName();

}
}

As per the preceding code, when these objects are configured with Spring, the EmployeeService object expects the Spring container to inject the object of Logger through the constructor. To achieve this, you need to set the configuration metadata as per the following snippet:

<?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">

<!-- All your bean and its configuration metadata goes here -->
<bean id="employeeService" class="com.packet.spring.constructor.di.EmployeeService">
<constructor-arg ref="logger"/>
</bean>

<bean id="logger" class="com.packet.spring.constructor.di.Logger">
</bean>

</beans>

In the preceding configuration, the Logger bean is injected into the employee bean through the constructor-arg element. It has a ref attribute, which is used to point to other beans with a matching id value.  This configuration instructs Spring to pass the object of Logger into the constructor of the EmployeeService bean.

You can put the <bean> definition in any order here. Spring will create the objects of <bean> based on need, and not as per the order they are defined here.

For more than one constructor argument, you can pass additional <constructor-arg> elements. The order is not important as far as the object type (class attribute of referred bean) is not ambiguous. 

Spring also supports DI with primitive constructor arguments. Spring provides the facility to pass the primitive values in a constructor from an application context (XML) file. Let's say you want to create an object of the Camera class with a default value, as per the following snippet:

public class Camera {
private int resolution;
private String mode;
private boolean smileShot;

//Constructor.
public Camera(int resolution, String mode, boolean smileShot) {
this.resolution = resolution;
this.mode = mode;
this.smileShot = smileShot;
}

//Public method
public void showSettings() {
System.out.println("Resolution:"+resolution+"px mode:"+mode+" smileShot:"+smileShot);
}
}

The Camera class has three properties: resolution, mode, and smileShot. Its constructor takes three primitive arguments to create a camera object with default values. You need to give configuration metadata in the following way, so that Spring can create instances of the Camera object with default primitive values:

<bean id="camera" class="com.packet.spring.constructor.di.Camera">
<constructor-arg type="int" value="12" />
<constructor-arg type="java.lang.String" value="normal" />
<constructor-arg type="boolean" value="false" />
</bean>

We pass three <constructor-arg> elements under <bean>, corresponding to each constructor argument. Since these are primitive, Spring has no idea about its type while passing the value. So, we need to explicitly pass the type attribute, which defines the type of primitive constructor argument.

In case of primitive also, there is no fixed order to pass the value of the constructor argument, as long as the type is not ambiguous. In previous cases, all three types are different, so Spring intelligently picks up the right constructor argument, no matter which order you pass them.

Now we are adding one more attribute to the Camera class called flash, as per the following snippet:

//Constructor.
public Camera(int resolution, String mode, boolean smileShot, boolean flash) {
this.resolution = resolution;
this.mode = mode;
this.smileShot = smileShot;
this.flash = flash;
}

In this case, the constructor arguments smileShot and flash are of the same type (Boolean), and you pass the constructor argument value from XML configuration as per the following snippet:

      <constructor-arg type="java.lang.String" value="normal"/>
<constructor-arg type="boolean" value="true" />
<constructor-arg type="int" value="12" />
<constructor-arg type="boolean" value="false" />

In the preceding scenario, Spring will pick up the following:

  • int value for resolution
  • String value for mode
  • First Boolean value (true) in sequence for first Boolean argument—smileShot
  • Second Boolean value (false) in sequence for second Boolean argument—flash

In short, for similar types in constructor arguments, Spring will pick the first value that comes in the sequence. So sequence does matter in this case. 

This may lead to logical errors, as you are passing wrong values to the right argument. To avoid such accidental mistakes, Spring provides the facility to define a zero-based index in the <constructor-arg> element, as per the following snippet:

      <constructor-arg type="java.lang.String" value="normal"
index="1"/>
<constructor-arg type="boolean" value="true" index="3"/>
<constructor-arg type="int" value="12" index="0"/>
<constructor-arg type="boolean" value="false" index="2"/>

This is more readable and less error prone. Now Spring will pick up the last value (with index=2) for smileShot, and the second value (with index=3) for flash arguments. Index attributes resolves the ambiguity of two constructor arguments having the same type.

If the type you defined in <constructor-arg> is not compatible with the actual type of constructor argument in that index, then Spring will raise an error. So just make sure about this while using index attribute.

..................Content has been hidden....................

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