Saturday, May 14, 2011

Dependency injection [Part 2]


Dependency Injection (DI)


Part 1 describes the basic concept of Dependency Injection and quick overview of spring framework.
Annotation and its strength described on blog Annotation, A Journey Novice to Ninja.
In this blog I have explained only those annotation which relevant to the demo example.
Spring introduces the following stereotypes to manage the various components.
Stereotype
Purpose
@Repository
It is use as a marker that fulfill the role of DAO layer
@Component
It is generic stereotype for any component managing by the spring framework
@Service
It is use as a marker that fulfills the role of the service Layer.
@Controller
It is use as a marker that plays the role of controller in MVC.

Let’s configure spring framework for the Traveler class whose class diagram as below

Code snippet 1

Traveler class declares the vehicle reference which will be injected into the Traveler class when instance of traveler class is requested to the spring framework.
package com.ninja.vehicle;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
 * @author Ashish.Chudasama
 */
@Service
public class Traveler {

     
      Vehicle vehicle = null;
      //setter injection for the Vehicle dependency
    @Autowired
    @Qualifier("Car")
      public void setVehicle(Vehicle vehicle) {
            this.vehicle = vehicle;
      }
      // start journey
      public void travel() {
            vehicle.move();
      }
                                      
}                                                           
Key point
1)      Annotating Traveler class with @Service ,  Spring framework treated traveler class as a service bean. Spring assigned default name to each bean, by default Bean name strategy generator generates name for each configured bean.
2)       setVehicle  marked with @Autowired and @Qualifier("Car") annotation
a.       @Autowired annotation wires the appropriate service bean during dependency resolution.
b.      @Qualifier annotation helps DI to inject appropriate service bean while resolving bean dependency. In case of Traveler, vehicle dependency resolved by identifying service name which is marked with “Car”.
c.       Qualifier helps to identify the appropriate bean instances if more than one implementation is available for the requested dependency.

Code snippet 2                                                                        

Vehicle class depends on Tier. Vehicle class delegates its move responsibility to rotate method of wheel implementer class.
package com.ninja.vehicle;

/**
 * @author Ashish.Chudasama
 */
public interface Vehicle {
      void move();
}

Code snippet 3


Bus is a concrete implementation of Vehicle. Bus class depends on Wheel and delegates its move task to rotate method of the wheel implementer sub-classes.
/**
 * @author Ashish.Chudasama
 */
package com.ninja.vehicle;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.novice.vehicle.component.Wheel;

@Service
@Qualifier(value="Bus")
@Lazy(value=true)
public class Bus implements Vehicle {
      //Wheel is dependency of Vehicle to move
      Wheel wheel;
      // setter based dependency
      @Autowired
      public void setWheel(Wheel wheel) {
            this.wheel = wheel;
      }

      @Override
      public void move() {
            System.out.println(this.getClass().getSimpleName());
            wheel.rotate();
      }
}

Key point
1)      Here Qualifier used to assigned “Bus” name to the Bus service alternatively we can also use @Service(“Bus”) both meaning are same.
2)      @Lazy annotation delays the initialization of bean until it’s required.

Code snippet 4

Car is a concrete implementation of Vehicle. Car class depends on Wheel and delegates its move task to rotate method of the wheel implementer sub-classes.
/**
 * @author Ashish.Chudasama
 */
package com.ninja.vehicle;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import com.novice.vehicle.component.Wheel;

@Service(value = "Car")
@Lazy
public class Car implements Vehicle {
      // Wheel is dependency of Vehicle to move
      Wheel wheel;

      // setter based dependency
      @Autowired
      public void setWheel(Wheel wheel) {
            this.wheel = wheel;
      }

      @Override
      public void move() {
            System.out.println(this.getClass().getSimpleName());
            wheel.rotate();
      }
}

Code snippet 5


A wheel class delegate is rotation function to actual Tier implementer classes to perform task of rotation.
package com.novice.vehicle.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

/**
 * @author Ashish.Chudasama
 */
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Wheel {

      //Tire is dependency of wheel class
      Tire tire=null;

      //Supply dependency using setter
      @Autowired
      @Qualifier(value="MRFTire")
      public void setTire(Tire tire) {
            this.tire = tire;
      }
     
      //function of wheel class
      public void rotate()
      {
            tire.rotate();
      }
}
Key point
1)      @Scope annotation used to specify the scope of the service.
2)      By default all service bean are singleton.
3)      Singleton and prototype only two scope available for non-web application.

Code snippet 6


Tire is an interface which ensures that all sub-classes or implementer of Tire can perform the task of rotation.
package com.novice.vehicle.component;

/**
 * @author Ashish.Chudasama
 */
public interface Tire {
      void rotate();
}

Code snippet 7


MRFTire is a concrete implementation of Tier interface.
package com.novice.vehicle.component;

import org.springframework.stereotype.Service;

/**
 * @author Ashish.Chudasama
 */
@Service(value="MRFTire")
public class MRFTire implements Tire {
      @Override
      public void rotate() {
            System.out.println(this.getClass().getSimpleName());
            System.out.println("Actual Work… (rotating tire)");
      }

}

Code snippet 8


JKTire is a concrete implementation of Tier interface.
package com.novice.vehicle.component;

import org.springframework.stereotype.Service;

/**
 * @author Ashish.Chudasama
 */
@Service(value="JKTire")
public class JKTire implements Tire {
      @Override
      public void rotate() {
            System.out.println(this.getClass().getSimpleName());
            System.out.println("Actual Work… (rotating tire)");
      }

}


Code snippet 9


This is test stub to check the spring dependency injection.
package com.ninja.vehicle;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * @author Ashish.Chudasama
 */
public class TestSpringDI {

      public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                        "springconfig.xml");
            BeanFactory factory = context;
            /**
* Default bean name generator generates name for the traveler class and which can be access as ‘traveler’
 */
            Traveler test = (Traveler) factory.getBean("traveler");
            test.travel();
            /**
             * Retrieve the bean instance using class
             */
            Traveler test = (Traveler) factory.getBean(Traveler.class);
            test.travel();
      }
}

Output
Car
MRFTire
Actual Work… (Rotating tire)
Car
MRFTire
Actual Work… (Rotating tire)



Code snippet 10

Spring configuration file
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

      <context:component-scan base-package="com" />

</beans>


Complete class diagram with annotation

Click here to download source code.

2 comments:

  1. again an interesting stuff... but this time i have a question..
    Whenever i come across Annotation, i feel it little boring to read and digest.

    It does reduces coding inside classes
    but don't you think after introducing annotation into the code, it again become coupled with one instance of implementer class whether it would be the Vehicle instance or Wheel instance.
    If tomorrow you want to use JKTyre instead of MRFTyre OR BUS instance instead of CAR as it's written over here, Don't you have to re-compile the code?

    ReplyDelete
  2. Also an overall idea behind introducing spring is to make it loosely coupled and loosely coupled means that changing behavious with a little or no change.

    here it is a little change but recompilation is what is very burdensome..

    ReplyDelete