CSC/ECE 517 Fall 2010/ch5 5e ms

From Expertiza_Wiki
Jump to navigation Jump to search

Dependency Injection

Java applications typically consist of objects that collaborate with each to form a proper application. Thus the objects in an application have dependencies on each other.

Dependency injection is way of providing the objects that it needs ie the dependencies that are needed instead of having it construct the dependencies by itself. Dependencies can injected in many ways either by using a specialized dependency injection frameworks like Spring or Google Guice or could be injected simply by instantiating and passing objects (dependencies) explicitly.

Types of Dependency Injection

1.Construction Injection 2.Setter Injection

Uses of Dependency Injection:

It's a very useful technique for testing, since it allows dependencies to be mocked or stubbed out. Simplies testing and improves decoupling.


Construtor Injection:

The basic idea with constructor-injection is that the object has no defaults and instead you have a single constructor where all of the collaborators and values need to be supplied before you can instantiate the object.


Lets start how injection is done using light weight container called Google guice


class RealBillingService implements BillingService {
 private final CreditCardProcessor processor;
 private final TransactionLog transactionLog;
 @Inject
 RealBillingService(CreditCardProcessor processor, 
     TransactionLog transactionLog) {
   this.processor = processor;
   this.transactionLog = transactionLog;
 }

}

We want to build a RealBillingService using PaypalCreditCardProcessor and DatabaseTransactionLog. Guice uses bindings to map types to their implementations.

public class BillingModule extends AbstractModule {
 @Override 
 protected void configure() {
    /*
     * This tells Guice that whenever it sees a dependency on a TransactionLog,
     * it should satisfy the dependency using a DatabaseTransactionLog.
     */
   bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    /*
     * Similarly, this binding tells Guice that when CreditCardProcessor is used in
     * a dependency, that should be satisfied with a PaypalCreditCardProcessor.
     */
   bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
 }

} The modules are the building blocks of an injector, which is Guice's object-graph builder. First we create the injector, and then we can use that to build the RealBillingService:

public static void main(String[] args) {
   /*
    * Guice.createInjector() takes your Modules, and returns a new Injector
    * instance. Most applications will call this method exactly once, in their
    * main() method.
    */
   Injector injector = Guice.createInjector(new BillingModule());
   /*
    * Now that we've got the injector, we can build objects.
    */
   RealBillingService billingService = injector.getInstance(RealBillingService.class);
   ...
 }

Advantage of having a constructor injection is it enforces order of initialization and prevents circular dependencies


Setter Injection

It is that you have a no argument-constructor which creates the object with "reasonable-defaults" . The user of the object can then call setters on the object to override the collaborators of the object in order to wire the object graph together or to replace the key collaborators with test-doubles.


class RealBillingService implements BillingService {
 private final CreditCardProcessor processor;
 public void setCreditCardProcessor(CreditCardProcessor processor){
  this.processor = processor
 }

}

class PaypalCreditCardProcessor{
 public void setPayPalAccount(Account account){
  this.account =account;
 }
}

The next step is to set up the configuration for the files. Spring supports configuration through XML files and also through code, but XML is the expected way to do it.

   <beans>
       <bean id="RealBillingService" class="spring.RealBillingService">
           <property name="processor">
               <ref local="CreditCardProcessor"/>
           </property>
       </bean>
       <bean id="CreditCardProcessor" class="spring.PaypalCreditCardProcessor">
           <property name="account">
               <value>1001</value>
           </property>
       </bean>
   </beans>


A simple test as follows

   public void testWithSpring() throws Exception {
       ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");
       RealBillingService service = (RealBillingService) ctx.getBean("RealBillingService");
       Products[] product = service.buyProduct("100");		// Argument is the amount to be debited for the purchase of products. 
       assertEquals("Once Upon a Time in the West", product.getTitle());
   }

When Should/Shouldn't be used

Why should/shouldn't be used