Using Guice with Play! Framework 2.1 for easy Dependency Injection

One of Play! Framework 2.1‘s latest features is the ability to construct controllers, so you can use dependency injection. Briefly, the benefits of using dependency injection is that you will have more modular, extensible, flexible, and testable code. At FortyTwo, we use Guice (a lightweight DI framework by Google) for many of our system components. I’ll show you how to easily integrate Guice into an example Play! 2.1 Scala project and then show you how this greatly improves your codebase’s flexibility.

TL;DR:

At its heart, dependency injection allows you to say something like “I’d like a DbConnection instance”, and not have to worry about where it comes from. Additionally, you can configure exactly which DbConnection to provide elsewhere. So, you can write your component very simply by injecting a DbConnection, and centrally configuring which implementation to use in which conditions. For development and testing, you may want to use an in-memory database, and for production, use a “real” database (example gist).

Integrating Guice with Play! Framework 2.1

For this example, we’ll create and test a very simple translation component. We’ll use a library called sse-guice to simplify Guice syntax, allowing us to use bind[Service].to[ServiceImpl].in[Singleton] instead of bind(classOf[Service]).to(classOf[ServiceImpl]).in(classOf[Singleton]). Ah, much prettier.

First, add Guice and sse-guice to your Build.scala app dependencies:

  val appDependencies = Seq(
    "com.google.inject" % "guice" % "3.0",
    "com.tzavellas" % "sse-guice" % "0.7.1"
  )

We’ll create a simple Translator trait, which will take an input String, and output its translation. For this demo, we’ll implement it with simple word-replacement for French.

trait Translator {
  def translate(input: String): String
}
 
class FrenchTranslatorImpl extends Translator {
  val wordReplacements = Map(
    "hello" -> "bonjour",
    "hi" -> "salut",
    "greetings" -> "salutations"
  )
  def translate(input: String): String = {
    input.split("""\s+""").map(word => wordReplacements.get(word.toLowerCase).map(newWord => word match {
        case _ if word(0).isUpper => newWord.capitalize
        case _ => newWord
      }).getOrElse(word)).mkString(" ")
  }
}

In a real application, you might implement translate() by calling a 3rd party API. Either way, we’ll treat this as something that’s blocking or is particularly slow. For testing, you might not be interested in the actual translation, but how parts of your application work with translated strings. So we’ll make a FakeTranslator (which just returns the input):

class FakeTranslator extends Translator {
  def translate(input: String): String = input
}

Next, we’ll define two modules for different circumstances, DevModule (development and testing) and ProdModule (for production). Whenever we need a translator in production, we’ll use the FrenchTranslatorImpl. Otherwise, we’ll use our more efficient FakeTranslator.

package common.modules
 
import com.tzavellas.sse.guice.ScalaModule
import common.translation._
 
class ProdModule extends ScalaModule {
  def configure() {
    bind[Translator].to[FrenchTranslatorImpl]
  }
}
 
class DevModule extends ScalaModule {
  def configure() {
    bind[Translator].to[FakeTranslator]
  }
}

Let’s create a new controller to use our (very rudimentary) translator.

package controllers
 
import play.api._
import play.api.mvc._
import common.translation.Translator
import com.google.inject._
 
@Singleton
class Translate @Inject()(translator: Translator) extends Controller {
  def greet(name: String) = Action {
    Ok(views.html.greet(translator.translate(s"Hello $name!")))
  }
}

You’ll notice a couple things are different than usual. Normally, Play! controllers are singleton objects, which cannot take parameters (so sadly, can’t be given injected classes). Here, we make Translate a class, and annotate it with @Inject(), which tells Guice to inject parameters. Next, we simply ask for a Translator instance. Notice we don’t ask which translator that we need.

The real magic happens in Global.scala, in a new method to Play 2.1 called getControllerInstance, which returns an instance of a requested controller (with Guice voodoo applied!). Additionally, we create the injector based on which mode Play is running in.

object Global extends GlobalSettings {
  private lazy val injector = {
    Play.isProd match {
      case true => Guice.createInjector(new ProdModule)
      case false => Guice.createInjector(new DevModule)
    }
  }
 
  override def getControllerInstance[A](clazz: Class[A]) = {
    injector.getInstance(clazz)
  }
}

Finally, to let Play! know to use getControllerInstance, we annotate the route with an @:

GET /greet/:name   @controllers.Translate.greet(name: String)

Now, when we start Play! in development mode, we get:

And in production mode:

So what? Why should I use DI with Play?

Notice that we needed no factories and were able to keep our code very nicely organized. We can now easily swap out Translator implementations with no mass code replace. But here’s a really cool benefit: Previously, testing Play! controllers was a lot harder and didn’t let us dynamically inject classes into controllers. We couldn’t easily swap out a HTTP client, email sender, or clock. However, now, we can very easily mock out classes when testing our controllers.

class TranslateSpec extends Specification {
 
  "Translate" should {
    // The normal Play! way
    "accept a name, and return a proper greeting" in {
      running(FakeApplication()) {
        val translated = route(FakeRequest(GET, "/greet/Barney")).get
 
        status(translated) must equalTo(OK)
        contentType(translated) must beSome.which(_ == "text/html")
        contentAsString(translated) must contain ("Barney")     
      }
    }
 
    // Providing a fake Global, to explitly mock out the injector
    object FakeTranslatorGlobal extends play.api.GlobalSettings {
      override def getControllerInstance[A](clazz: Class[A]) = {
        new Translate(new FakeTranslator).asInstanceOf[A]
      }
    }
    "accept a name, and return a proper greeting (explicitly mocking module)" in {
      running(FakeApplication(withGlobal = Some(FakeTranslatorGlobal))) {
        val home = route(FakeRequest(GET, "/greet/Barney")).get
        contentAsString(home) must contain ("Hello Barney")
      }
    }
 
    // Calling the controller directly, without creating a new FakeApplication
    // (This is the fastest)
    "accept a name, and return a proper greeting (controller directly, no FakeApplication!)" in {
      val controller = new Translate(new FakeTranslator)
      val result = controller.greet(name = "Barney")(FakeRequest())
      contentAsString(result) must contain ("Hello Barney")
    }
  }
}

The first way is the typical way Play! controllers are tested. The second is using a custom Global, which explicitly sets the getControllerInstance to return a FakeTranslator instance. Using this pattern, you can easily mock classes that you are testing around. A common use-case is mocking a HTTP client with a class that gives a pre-defined response. Finally, the 3rd method avoids starting a FakeApplication entirely, and instead calls the controller directly with our FakeTranslator.

Conclusion

Play! 2.1 has opened the door for developers to better organize and more thoroughly test their code using dependency injection. Because we can now construct controllers with injected classes, controllers can be more thin and tests are significantly faster. Check out the Github repo for this example, which you can use to bring Guice to your own projects.

Post comment as twitter logo facebook logo
Sort: Newest | Oldest
D_Roch 5 pts

great article ! thank you.

Is it also possible to do the injection without having to pass by a route ?

Andrew Conner 8 pts

 D_Roch If I'm understanding this correctly, yes. See the last example ("Calling the controller directly, without creating a new FakeApplication"), which doesn't use routes at all.

D_Roch 5 pts

 Andrew Conner yes that's what I was looking for, however I don't have acces to FakeTranslator as its defined in the application consuming my module. Any idea how I can spawn it in a magic way like in the routing example ?

Andrew Conner 8 pts

 D_Roch You can either write dependencies in a way so that they can be constructed, or you could use the injector directly (as `Global` does). For convenience, we have an injector provider for testing (which simply does `Guice.createInjector(new TestModule)`). So you can do something like:

 

withInjector { implicit injector =>

    inject[SomeInjectedClass].useIt()

}

D_Roch 5 pts

 Andrew Conner thank you for your help, what do you have in the withInjector method ?

Andrew Conner 8 pts

 D_Roch It simply creates an injector and passes it to the provided closure. Here's some of the code (you may not need something quite as robust): https://gist.github.com/andrewconner/4ef1eac999c73a0e97d3

KorrawitYindee 5 pts

Great explanation ! Is it possible to do DI in Play Java ?

Andrew Conner 8 pts

 KorrawitYindee Absolutely. The mechanics are the same, just slightly different syntax. Guice was written for Java, so works just as cleanly. Check out Guice's docs for that syntax. For Play!, routes are the same, controllers are the same (adding @Inject annotations with injected parameters), and you can still use `getControllerInstance`.

 

See: https://github.com/playframework/Play20/wiki/JavaInjection

Thanks for the post ! But is it still possible to use reverse routing (I don't think so but I may be wrong) ?

Andrew Conner 8 pts

Yep, reverse routing has no problems. Here's Play!'s implementarion of ReverseTranslator: https://gist.github.com/74f8a29d34df858e2b3d

 

As you can see, Play calls `getControllerInstance` again (asking for a `controllers.Translate`).