-
-
Notifications
You must be signed in to change notification settings - Fork 0
NeuronDIAtPlayForScalaTutorial
This tutorial explains how to use Neuron DI in a Play for Scala application with the Guice framework. It is assumed that you are already familiar with Guice. If not, please consult the Guice Wiki first. You do not need to be familiar with Neuron DI yet. However, unlike other tutorials, this tutorial is not a comprehensive introduction to Neuron DI, so reading the Neuron DI @ Guice For Scala Tutorial afterwards is recommended. The source code for this tutorial is available on GitHub.
Please read the project setup page first.
Make sure your project enables the NeuronDIAtGuiceForScalaPlugin
, e.g. like this:
lazy val root = (project in file(".")).enablePlugins(PlayScala, NeuronDIAtGuiceForScalaPlugin)
Note that there is no specific plugin for Neuron DI @ Play For Scala - it's not required. Now let's write a controller which has its dependencies injected by Neuron DI:
package controllers
import global.namespace.neuron.di.scala._
import models.Greeting
import play.api.i18n.Langs
import play.api.libs.json.Json
import play.api.mvc.BaseController
import play.twirl.api.Html
import services.GreetingService
@Neuron
trait GreeterController extends BaseController {
def greetingService: GreetingService
def langs: Langs
lazy val greetingsList: Seq[Greeting] = Seq(
Greeting(1, greetingService.greetingMessage("en"), "sameer"),
Greeting(2, greetingService.greetingMessage("it"), "sam")
)
def greetings = Action {
Ok(Json.toJson(greetingsList))
}
def greetInMyLanguage = Action {
Ok(greetingService.greetingMessage(langs.preferred(langs.availables).language))
}
def index = Action {
Ok(Html("<h1>Welcome</h1><p>Your new application is ready.</p>"))
}
}
At first sight, this doesn't look very different from traditional DI with JSR-330:
In a JSR-330 framework like Guice, this would be a class with a constructor with two parameters of the type
GreetingService
and Langs
and an @Inject
annotation.
Let's go through this in detail:
- The
import global.namespace.neuron.di.scala._
statement makes the Scala API of Neuron DI available. - The
@Neuron
annotation is required for Neuron DI to apply its magic. A class or trait annotated with this annotation is called a neuron class or neuron trait. -
GreeterController
is a trait instead of a class. This is a conscious design decision which generally facilitates reuse. You could also use an abstract class instead. - There is no
@Inject
annotation because Neuron DI has no use for it and hence doesn't even know about it. - Dependencies are modeled as abstract, parameterless methods - in this case
greetingService: GreetingService
andlangs: Langs
. Such methods are called synapse methods because, in analogy with biology, that's were neuron classes make contact with their dependencies. - If you want any of these dependencies to be cached for reuse you could simply change their definition from
def
toval
or declare them as a singleton using the Guice binding DSL (domain specific language). -
greetingsList
is defined as alazy val
instead ofval
. As with any Scala trait, this is critical or otherwise this value would be initialized before Neuron DI could even initialize its dependency resolution mechanism and hence aNullPointerException
would occur. This is a general constraint implied by Scala traits, not Neuron DI.
Last but not least, we need to tell Guice to use the Neuron DI framework for instantiating the GreeterController
class
and wire its dependencies:
import controllers.GreeterController
import global.namespace.neuron.di.guice.scala._
class Module extends NeuronModule {
override def configure(): Unit = {
bindClass[GreeterController].toNeuronClass[GreeterController]
// alias:
//bindNeuronClass[GreeterController]
}
}
Lets go through this in detail again:
- The
import global.namespace.neuron.di.guice.scala._
statement makes the Scala API of Neuron DI @ Guice available. - The class is called
Module
and it's placed in the default package. This is the exact name where the Play framework expects the application to put a custom Guice module, if any. - The class extends
NeuronModule
. Together with the import statement, this is required to make the custom binding DSL available. - The single
bindClass(...).toNeuronClass(...)
statement binds the traitGreeterController
to itself as a neuron class. This is required to make Guice aware thatGreeterController
needs to be instantiated and wired by Neuron DI. - Because the interface and implementation classes are the same, the
bindClass(...).toNeuronClass(...)
statement could be simplified to a singlebindNeuronClass(...)
call as shown in the comment. -
GreetingService
is a non-abstract class with a default constructor, so Guice will figure a Just-in-time binding for it and hence no binding statement is required in this module. - The Play framework provides a built-in binding for the
Langs
class, so again, no binding statement is required in this module.
That's all it needs to use Neuron DI in a Play for Scala application with the Guice framework. For more details, please read the Neuron DI @ Guice For Scala Tutorial.