Saturday, April 2, 2011

Google Guice

As a developer and designer, you want to reduce static dependencies between classes. Whenever you need an instance, you still have to refer to the real class in order to get an instance of the implementation.

ExampleInterface object = new ExampleImpl();

We are only interested in the interface, so we want to avoid having to know the real implementation class. We can use a Factory Method design pattern to achieve this. Using this design pattern, we centralize and hide the implementation class in the factory class. This enables us to vary the implementation if needed. This is particular useful, when using mock objects for testing the application. Using Factory Methods reduces coupling and increases modularity.

public class ExampleFactory() {
public ExampleInterface getInstance() {
return new ExampleImpl();
}
}

The factory is commonly implemented as a Singleton.

A better way to couple objects together, is to use Dependency Injections. In JEE, this is available in a container for container managed classes. In the example code below, the container will automatically create and set a resource instance of the desired class.

public class Client {
@Resource
private ExampleInterface instance;
}

In other classes where container Dependency Injection is not possible, we can pass the implementation to the client class by using its constructor.

public class Client {
private ExampleInterface instance;

public Client(ExampleInterface instance) {
this.instance = instance;
}
}

The problem here is, that we need the implementation class whenever we instantiate the client class. This ties our implementation class to every location where we instantiate the client class, which we really want to avoid.

In this blog post, we'll use Google Guice to leverage Dependency Injection in the rest of our code. In order to use Guice, we need a class that maps interfaces to their implementation. The class has to extend AbstractModule provided by Guice.

public class ExampleModule extends AbstractModule {
@Override
protected void configure() {
bind(ExampleInterface.class).to(ExampleImpl.class);
bind(AnotherClass.class).to(AnotherInterface.class);
}
}

Now, we can use the @Inject annotation in places, where we need the implementations of mapped interfaces. The class below shows how.

public class ExampleClient {
// Attribute Injection
@Inject
private ExampleInterface instance;

// Constructor Injection
@Inject
public ExampleClient(ExampleInterface inst1) {
...
}

// Method Injection
@Inject
public void ExampleMethod(ExampleInterface inst2) {
...
}
}

Now, when we instantiate the client class, we let Guice to instantiate it.

Injector injector = Guice.createInjector(new ExampleModule());
ExampleClient client = injector.getInstance(ExampleClient.class);

Guice will create and inject the dependencies before returning the client class.

We can also use the injector as a factory.

ExampleInterface instance = injector.getInstance(ExampleInterface.class);

A complete example application can be found below. Make sure the following JAR-files are included in your build path when compiling the example:

  • guice-3.0.jar
  • javax.inject.jar
  • aopalliance.jar

ExampleClient.java

package com.javaeenotes;

import com.google.inject.Inject;

public class ExampleClient {
// Attribute Injection
@Inject
private ExampleInterface instance;

// Constructor Injection
@Inject
public ExampleClient(ExampleInterface inst1) {
inst1.method("Constructor is successfully injected.");
}

// Method Injection
@Inject
public void ExampleMethod(ExampleInterface inst2) {
inst2.method("Method is successfully injected.");
}

// Used to test Attribute Injection
public void testAttribute() {
instance.method("Attribute is successfully injected.");
}
}

ExampleImpl.java

package com.javaeenotes;

public class ExampleImpl implements ExampleInterface {

@Override
public void method(String s) {
System.out.println(s);
}
}

ExampleInterface.java

package com.javaeenotes;

public interface ExampleInterface {
public void method(String s);
}

ExampleModule

package com.javaeenotes;

import com.google.inject.AbstractModule;

public class ExampleModule extends AbstractModule {
@Override
protected void configure() {
bind(ExampleInterface.class).to(ExampleImpl.class);
}
}

GuiceDemo.java

package com.javaeenotes;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class GuiceDemo {

public static void main(String[] args) {
Injector injector = Guice.createInjector(new ExampleModule());

System.out.println("Getting instance using injector as a factory.");
ExampleInterface instance = injector
.getInstance(ExampleInterface.class);
instance.method("Test instance returned by injector.");

System.out.println("Getting injected client.");
ExampleClient client = injector.getInstance(ExampleClient.class);
client.testAttribute();
}
}

No comments:

Post a Comment