April 10, 2011

Google Guice 3.0

Google Guice is a very interesting framework. Some folks look him as a replacement for Spring Framework. I really don't think that is the case, Spring is a great kick ass framework really big with lots of utilities like a swiss army knife. Spring have a huge ecosystem, Guice is not a replacement, IMHO I see Guice as a very lightweight simple object register dependency resolution engine.  It can be very cool and useful in some specific scenarios like coding a Android solution or even a Java web application.

It's really fast and simple to use it. Guice embrace Java annotations implementing the JSR330 for Java common annotation and dependency injection annotations like @Inject, @Qualifier, @Singleton, @Named, @Scope and others.

You use annotations to wire up your components & POJOs and use a simple DSL to define your object graph dependencies. This dependencies can be assemble with lots of flexibility because is Java code. It's possible to split the configuration in several Java classes(called Modules), code readability is a clear benefit that you get using Google Guice.


Let's see some code and have fun with it. This is a simple Java interface. I'm using that because will inject it later on the code. So as you can see nothing new here.

package com.github.diegopacheco.sandbox.java.giuce3.service;
/**
 * Service Interface
 * @author Diego Pacheco
 *
 */
public interface DateService {
public String currentDate();
}



Now let's look a default implementation that I created for that class. Look the code below. Again there nothing new here, no Guice code yet.


package com.github.diegopacheco.sandbox.java.giuce3.service;

/**
 * Default implementaion of DataService
 * @author Diego Pacheco
 *
 */
public class DateServiceImpl implements DateService{
  @Override
  public String currentDate() {
    return new java.util.Date().toString();
 }
}


And there is a second implementation here, look the code below.


package com.github.diegopacheco.sandbox.java.giuce3.service;

import javax.inject.Singleton;
/**
 * Logging implementation for the DateService
 * @author Diego Pacheco
 *
 */
@Singleton
public class LoggingDateService implements DateService{
  @Override
  public String currentDate() {
      String result = new java.util.Date().toString();
      System.out.println("Return date: " + result);
      return result;
  }
}



OK, lets see some Guice code now, look the class below. The @Singleton annotation will be explained after the next code.


package com.github.diegopacheco.sandbox.java.giuce3.service;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

/**
 * Using @Inject from java to inject via guice
 *
 * @author Diego Pacheco
 *
 */
@Singleton
public class DateTimeService {

    private DateService ds;
    private LoggingDateService lds;
    private String javahome;

    @Inject
    public DateTimeService(DateService ds, @Named("lds") LoggingDateService lds){
          this.ds = ds;
          this.lds = lds;
    }

    @Inject
    public void setJavahome(@Named("JH") String javahome) {
          this.javahome = javahome;
    }

    public String showDateAndTime(){
          return ds.currentDate();
    }

    public String showDateAndTimeLogged(){
          System.out.println("BTW: JAVA HOME is " + javahome);
          return lds.currentDate();
    }

}





First you can see that the class DateTimeService has the annotation @Singleton, this means that we gonna have just one instance for this class, this is a standard optimization used in java application. You also may notice the @Inject annotation in two different places, in the set method called setJavahome, means that we want Giuce inject a dependency for us there in that case we're talking about a String.

Second you can see the @Inject annotation on the constructor, Guice will inject all dependencies for that constructor. We don't know what will be inject, that will be unfold when we see the modulo with the Guice DSL later.

There is a @Named annotation also, this is one way to make injection in a qualified way, because something you can have multiples injections or possibilities for the same resource, this annotation let use use different values consistently.

So, let's see the dependency inject DSL, look the code below.


package com.github.diegopacheco.sandbox.java.giuce3.service.module;

import com.github.diegopacheco.sandbox.java.giuce3.service.DateService;
import com.github.diegopacheco.sandbox.java.giuce3.service.DateServiceImpl;
import com.github.diegopacheco.sandbox.java.giuce3.service.DateTimeService;
import com.github.diegopacheco.sandbox.java.giuce3.service.LoggingDateService;
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;

/**
 * Guice Module for DataService
 * @author Diego Pacheco
 *
 */
public class DateServiceModule extends AbstractModule {

    @Override
    protected void configure() {
          bind(DateService.class).to(DateServiceImpl.class);
          bind(DateTimeService.class);    bind(LoggingDateService.class).annotatedWith(Names.named("lds")).to(LoggingDateService.class);
bind(String.class).annotatedWith(Names.named("JH")).toInstance(System.getProperty("JAVA_HOME","D:\\"));
    }

}


*bind* is the key method to register beans. You basically register a java interface to a particular interface, in the first line i'm telling that DateService interface will be implemented and provided by DataServiceImpl, yes that class will be inject in the previous code.

Them we can see this strange code bind(DateTimeService.class); I'm doing this because DateTimeService is a concrete class and it serve it self, so this is the syntax to define that. For the next line you see the code annotatedWith(Names.named("lds")) this is used to define a named injection so you can get this dependency using the @Named annotation showed in the previous code too.

For least we see that Guice can work with standard classes like String and this one is defined using the same rule as the last one. So now we just need boot up Guice and start the fun :D

package com.github.diegopacheco.sandbox.java.giuce3.service.test;

import junit.framework.Assert;

import org.junit.Test;

import com.github.diegopacheco.sandbox.java.giuce3.service.DateTimeService;
import com.github.diegopacheco.sandbox.java.giuce3.service.module.DateServiceModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

/**
 * Test Guice 3
 * @author Diego Pacheco
 *
 */
public class DateTimeServiceTest {

    @Test
    public void testShowDateAndTime(){

      Injector injector   = Guice.createInjector(new DateServiceModule());
      DateTimeService dts = injector.getInstance(DateTimeService.class);

      Assert.assertNotNull("Google Guice Ibjector could not be null",injector);
      Assert.assertNotNull("DateTimeService could not be null",dts);

      System.out.println(dts.showDateAndTime());

    }

    @Test
    public void testShowDateAndTimeLogging(){

      Injector injector   = Guice.createInjector(new DateServiceModule());
      DateTimeService dts = injector.getInstance(DateTimeService.class);

      Assert.assertNotNull("Google Guice Ibjector could not be null",injector);
      Assert.assertNotNull("DateTimeService could not be null",dts);

      System.out.println(dts.showDateAndTimeLogged());
    }
}



Here we can see a JUnit test case. You just need 2 lines to boot up Guice and get a Dependency from him:

Injector injector   = Guice.createInjector(new DateServiceModule());
DateTimeService dts = injector.getInstance(DateTimeService.class);


You just create a Guice Injector providing your modules and them you can use the injector to get dependencies by class names. This is it. You can get the full code on my github repository, there you find the complete project for eclipse with maven 3 as build system.

Cheers,
Diego Pacheco

1 comment:

Christophe Marchal said...

Nice Guice introduction. Short and enough to get the taste :)