Playframework + Google Guice


Nota: este artículo también está disponible en español.

In the project I’m currently working, we started to use Google Guice. For those who don’t know, Google Guice is a dependency injection framework. The basic idea behind dependency injection is to provide a class it’s dependencies, instead of making the dependent class responsible of instantiating the objects on which it depends.

Play has a module for integrating Guice:

http://www.playframework.org/modules/guice-1.2/home

In addition to the module documentation, this post from @_felipera can help you get started.

http://geeks.aretotally.in/dependency-injection-with-play-framework-and-google-guice

How to use the Guice module

Add the dependency

require:
  - play
  - play -> guice 1.2

Download the dependencies

play deps

Create a new class that will be injected in the controller

services.MyService

package services;
public interface MyService {
   public void sayHello();
}

services.MyServiceImpl

package services;
public class MyServiceImpl implements MyService {
    public MyServiceImpl(){
        play.Logger.info("constructor!");
    }
    
    @Override
    public void sayHello() {
        play.Logger.info("hello");
    }
}

Configure the injector

package config;
public class GuiceConfig extends GuiceSupport {
    @Override
    protected Injector configure() {
        return Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(MyService.class).to(MyServiceImpl.class).in(Singleton.class);
            }
        });
    }
}

This will setup the class as a singleton. Each time a class has the dependency of MyService, the injector will inject the same instance of MyServiceImpl.

Inject the dependency using the @Inject annotation

package controllers;
public class Application extends Controller {

    @Inject
    static MyService myService;

    public static void index() {
        myService.sayHello();
        render();
    }
}

Testing

My next step was to create a test and here came the first problem

play test

http://localhost:9000/@tests

Compilation error! The problem is that the module has a folder called ‘test’. This folder should have some unit or functional test, but instead it has three sample applications. The convention in play modules is to have these kind of applications in the ‘samples-and-test’ folder.
I made a fork of the project to rename this folder:

https://github.com/axelhzf/play-guice-module

I also did a pull-request but I didn’t get any response so far :

https://github.com/pk11/play-guice-module/pull/5

Renaming the folder ‘test’ was enough to run this test:

@InjectSupport
public class InjectTest extends UnitTest {
    @Inject
    static MyService myService;

    @Test
    public void injectOk(){
        assertNotNull(myService);
    }
}

Adding more dependencies

By default, Play automatically detects the @Inject annotation on classes than inherit from Controller, Job and Mail. If you want to inject dependencies on other classes you must use the @InjectSupport.

Typically our services are not as simple as MyService. It is common to have dependencies between services. Guice solves this problem analyzing the dependencies and instantiating the objects in the proper order.

services.MyDependentService

package services;

public interface MyDependentService {
    public void sayHelloWorld();
}

service.MyDependentServiceImpl

package services;

@InjectSupport
public class MyDependentServiceImpl implements MyDependentService {

    @Inject
    static MyService myService;

    public MyDependentServiceImpl(){
        play.Logger.info("Init MyDependentServiceImpl");
    }

    public void sayHelloWorld(){
        myService.sayHello();
        play.Logger.info("world");
    }
}

InjectTest

@InjectSupport
public class InjectTest extends UnitTest {

@Inject
static MyDependentService myDependentService;

@Test
public void injectOk(){
    assertNotNull(myDependentService);
    myDependentService.sayHelloWorld();
}

}

Binding in GuiceConfig

    bind(MyDependentService.class).to(MyDependentServiceImpl.class).in(Singleton.class);

And this is the console output

20:34:39,090 INFO ~ Init MyServiceImpl
20:34:39,095 INFO ~ Init MyDependentServiceImpl
20:34:39,095 INFO ~ Application 'lazySingleton' is now started !
20:34:39,136 INFO ~ hello
20:34:39,136 INFO ~ world

Constructor injection

One of the things I don’t like about the module is that the fields you are allowed to inject must be static. I would prefer to declare the dependencies as constructor parameters. This way, it would be more obvious that to create an instance of MyDependentServiceImpl you need a MyService instance. Moreover, when working on units test, it’s easier to pass a mock objects as a parameter than to configure an injector.

In the module documentation I didn’t found any reference to how to do it. Nevertheless, I found an article explaining how to do this using a Provider:

http://ericlefevre.net/wordpress/2011/05/08/play-framework-and-guice-use-providers-in-guice-modules/

Later, I found a question on StackOverflow that gave me another clue:

http://stackoverflow.com/questions/8435686/does-injector-getinstance-always-call-a-constructor

In the Edit he says that he forgot to put the @Inject annotation in the constructor. I tried to do the same thing and finally it worked:

public class MyDependentServiceImpl implements MyDependentService {

    private final MyService myService;

    @Inject
    public MyDependentServiceImpl(MyService myService){
        this.myService = myService;
        play.Logger.info("Inicializando MyDependentServiceImpl");
    }

    ...

Lazy Singletons

It still remains one last detail to have a perfect google guice configuration.

The services are initialized when the application starts.

21:38:11,801 INFO ~ Inicializando MyServiceImpl
21:38:11,805 INFO ~ Inicializando MyDependentServiceImpl
21:38:11,805 INFO ~ Application 'lazySingleton' is now started !

When the application is in production mode this is the correct behavior. But in development mode I prefer that Singletons are intialized on demand. There may be services that take their time to start and I want the application startup to be as fast as possible.

With Google Guice you can achieve this using Scopes:

http://code.google.com/p/google-guice/wiki/Scopes

All you have to do is to set the Stage parameter:

Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
return Guice.createInjector(stage, new AbstractModule() {…..

Rerunning the test

22:00:03,353 WARN ~ You're running Play! in DEV mode
22:00:04,615 INFO ~ Connected to jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
22:00:04,811 INFO ~ Guice injector created: config.GuiceConfig
22:00:04,819 INFO ~ Init MyServiceImpl
22:00:04,824 INFO ~ Init MyDependentServiceImpl
22:00:04,824 INFO ~ Application 'lazySingleton' is now started !

Ooops! The Singleton are initialized before application starts. Perhaps that’s not the correct use for the stage variable. Let’s try a test:

public class StageTest {

    @Test
    public void testDevelopment(){
        Injector injector = createInjector(Stage.DEVELOPMENT);
        System.out.println("development - before getInstance");
        MyService instance = injector.getInstance(MyService.class);
        System.out.println("development - after getInstance");
    }

    @Test
    public void testProduction(){
        Injector injector = createInjector(Stage.PRODUCTION);
        System.out.println("production - before getInstance");
        MyService instance = injector.getInstance(MyService.class);
        System.out.println("production - after getInstance");
    }

    public Injector createInjector(Stage stage){
        Injector injector = Guice.createInjector(stage, new AbstractModule(){
            @Override
            protected void configure() {
                bind(MyService.class).to(MyServiceImpl.class);
            }
        });
        return injector;
    }
}

And the result is:

INFO: development - before getInstance
INFO: Inicializando MyServiceImpl
INFO: development - after getInstance

INFO: Inicializando MyServiceImpl
INFO: production - before getInstance
INFO: production - after getInstance

Just like it says on the documentation, in development mode Singletons are initialized lazily.

If this works, when I tried using the play module why didn’t it work?

Reviewing the code:

https://github.com/pk11/play-guice-module/blob/master/src/play/modules/guice/GuicePlugin.java

@OnApplicationStart the module finds all classes annotated with @InjectSupport and inject it’s dependencies. To inject the dependencies the module calls the getBean() method. And here we find the problem: when calling getBean() the class is instantiated.

I found a solution to this problem:

https://groups.google.com/d/msg/google-guice/405HVgnCzsQ/fBUuueP6NfsJ

This is the code:

These classes create a proxy for each class annotated as @LazySingleton. When the object is injected the injector actually injects the proxy. The first time a method is invoke, the proxy will take care of initializing the class.
Using these classes, the injector configuration looks like:

public class GuiceConfig extends GuiceSupport {
    @Override
    protected Injector configure() {
        Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
        return Guice.createInjector(stage, new AbstractModule() {
            @Override
            protected void configure() {
                bindScope(LazySingleton.class, MoreScopes.LAZY_SINGLETON);
                bindLazySingletonOnDev(MyService.class, MyServiceImpl.class);
                bindLazySingletonOnDev(MyDependentService.class, MyDependentServiceImpl.class);
            }

            protected <T> void bindLazySingletonOnDev(Class<T> expected, Class<? extends T> implClass){
                if(Play.mode.isDev()){
                    bind(implClass).in(MoreScopes.LAZY_SINGLETON);
                    Provider<T> provider = LazyBinder.newLazyProvider(expected, implClass);
                    bind(expected).toProvider(provider);
                }else{
                    bind(expected).to(implClass).in(Scopes.SINGLETON);
                }
            }
        });
    }
}

I will add this classes to the fork in order to have a complete module that can be reused across projects.

Conclusion

In the last few years Dependency Injection has has evolved from being an obscure and little understood term, to become part of every programmer’s toolchest. In this article we saw how easy is to integrate Guice, a very handy library from Google, into a play framework application. Moreover, we also covered how to customize it’s behaviour for a better development experience.

Article original published at http://axelhzf.tumblr.com.

About these ads

6 responses to this post.

  1. […] « First steps with Scala revisited once more: Scala’s implicit revenge Playframework + Google Guice […]

    Reply

  2. Great article, Axel

    Usually the hardest part of adopting dependency injection, is understanding WHY do we even need it. At google guice page, there’s an excellent introduction explaining it with a very clear example that talks about factory pattern, testability, moduarity and, of course, dependency injection.

    Dont’ miss it: http://code.google.com/p/google-guice/wiki/Motivation

    Reply

  3. Playframework + Google Guice « Having fun with Play framework!…

    Thank you for submitting this cool story – Trackback from JavaPins…

    Reply

  4. Reblogged this on A pinch of curiosity and commented:
    This is quite amazing! Exactly what I’ve been waiting for!

    Reply

  5. Nice – the LazySingleton is a nice touch for dev. People should check his later post about how to use his version of the guice module at:

    http://axelhzf.tumblr.com/post/17041500120/ampliando-el-modulo-de-google-guice

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: