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.

We wrote this post while working on Kifi — tools that help people outsmart information overload together. Learn more.

19 comments
Alberto Souza
Alberto Souza

Hi, this is a interesting topic. I don't like dependency injection in controllers, especially in Play apps. Everything is based on environment, you can switch translator inside a Factory in the same way you changed the Guice configuration class... Why is this better? Your controller, in most times, shouldn't do business logic, just delegate responsabilities for all your other classes. I wrote a post discussing this topic as well :)(http://alots.wordpress.com/2014/03/12/strategies-for-testing-static-calls-in-play-projects/). It is funny because we have different thoughts about the same topic.

MarkusJura
MarkusJura

Great post!

How do I initialize a guice class in an scala object?

One use case would be if I need to call a guice class in the onStart method of the Global object.

tianjianfeng
tianjianfeng

Hi Andrew, 

 

Thanks for the post. 

 

Let's assume the translate method only return Boolean, like below

 

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

 

How would you test if translate method return false in the unit test? 

 

Thanks

D_Roch
D_Roch

great article ! thank you.

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

KorrawitYindee
KorrawitYindee

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

Nico
Nico

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
Andrew Conner

 @BalazsMariaNemeth Models aren't special, so instead of using an object, use a class with injection. If you'd like it to be a singleton, you can annotate with `@Singleton`. i.e.:

 

 @Singleton

class User @Inject(pref: UserPreference, email: UserEmail) {

  ...

Andrew Conner
Andrew Conner

 @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.

Andrew Conner
Andrew Conner

 @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

D_Roch
D_Roch

 @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 ?

BalazsMariaNemeth
BalazsMariaNemeth

 @Andrew Conner Nice one. It answers some of my questions I have right now about this stack and also the ones I managed to answer myself in the last couple of weeks.

I think it is a very powerful thing to have a working framework (the way you use the stack), especially that the so called best practices aren't here yet. Or at least it doesn't take 2 minutes to find them like it would in a Java EE environment. But in the end the challenge and the prize is bigger as well :)

Andrew Conner
Andrew Conner

 @BalazsMariaNemeth Ah. Couple of things: mixing normal classes with injected classes doesn't play super nice. The easiest option, changing the least amount of code:

@Singleton class Guests @Inject(dep1: Dep1, dep2: Dep2) extends CRUDModel[User]("GUESTS", dep1, dep2)

 

And then receive the dependencies in CRUDModel. Less than perfect, but works and bridges the gap between injected and normal classes.

 

The better solution: We weren't very happy with the default / proposed patterns Slick has in their documentation. Instead of extending the Table class for your model, use a variable inside your model:

 

  override val table = new RepoTable[Guest](db, "GUEST") {

    def user = column[Long]("user_id", O.NotNull) 

    ....

    def * = id.? ~ createdAt ~ updatedAt ~ user [etc]

  }

 

This has an added benefit of making it easier to pass tables around for joins.

D_Roch
D_Roch

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

Andrew Conner
Andrew Conner

 @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()

}