Dependency injection with Scaldi
2016-02-23
Scaldi is a lightweight dependency injection framework for Scala. In Play, it provides a more Scala-like approach to injecting dependencies than the out of the box solution based on Google Guice.
Installation
My examples are based on a so-called starting-point
. The starting-point
is basically a slightly simplified Activator template app. If you want to follow, just clone the book's repo and create a new branch:
$ git clone https://github.com/denisftw/modern-web-scala .
$ git checkout tags/starting-point -b blog-branch
The official Scaldi website suggests we add two libraries - the library itself and the Play integration part:
libraryDependencies ++= Seq(
// ...
"org.scaldi" %% "scaldi" % "0.5.7",
"org.scaldi" %% "scaldi-play" % "0.5.13"
)
After that we should inform Play that we're going to use this library and not the default one in application.conf
:
play.application.loader = scaldi.play.ScaldiApplicationLoader
play.modules.enabled += modules.MainModule
Here we're specifying the ScaldiApplicationLoader
as the application loader and adding a module that will contain our services.
Defining modules
We specified our module in the configuration file, but it doesn't exist. Let's create a file called MainModule
in package modules
:
class MainModule extends Module {
binding to new Application
}
And that's it. After reloading, Play will download necessary dependencies from the Internet and Scaldi will start managing the app.
Managing the application lifecycle
In early days of Play, there was a trait
called GlobalSettings
, which you would use for registering lifecycle callbacks:
object Global extends GlobalSettings {
override def onStart(app: Application) {
super.onStart(app)
}
override def onStop(app: Application) {
super.onStop(app)
}
}
In Play 2.4, this approach is deprecated and we need to use something else. Fortunately, Scaldi offers so-called eagerly initialized bindings:
class MainModule extends Module {
// ...
binding toNonLazy new Initializer initWith(_.init()) destroyWith(_.stop())
}
And the good thing about it is that Initializer
could be a simple class and doesn't even have to extend any traits:
class Initializer {
def init() = {
Logger.info("Starting...")
}
def stop() = {
Logger.info("Stopping...")
}
}
Here we're simply printing log messages, but it's certainly possible to do something more useful.
Injecting services
The most common way to initialize a new service is to use lazy binding. Let's add a new service in the MainModule
trait:
class MainModule extends Module {
// ...
binding to new GreetingService
}
The service itself is just a class. If we're not going to inject other services into GreetingService
itself, it could be very simple:
class GreetingService {
def greet(name: String): String = s"Hello $name"
}
After the GreetingService
is defined inside the module, we can inject it into the Application
controller:
class Application(implicit inj: Injector) extends Controller with Injectable {
val greetingService = inject[GreetingService]
def greet(name: String) = Action {
val greeting = greetingService.greet(name)
Ok(Json.toJson(greeting))
}
// ...
}
The trick here is to add an implicit Injector
parameter to the Application
controller. Extending the Injectable
trait makes the inject
method available. In my opinion, this approach looks much nicer than what Guice offers.
There are many more features that are available in Scaldi. Definitely check it out if you need a runtime DI and/or you don't feel like using Guice annotations in Scala code.
Concusion
This is the first post in a series of posts about the Scala ecosystem. Next time, I'm going to integrate ScalikeJDBC into this sample application. Also, if you want to get familiar with the Scala ecosystem while learning the language, take a look at my book Modern Web Development with Scala.