Dependency Injection - Scala
Syntax#
- class MyClassUsingAnother @Inject() (myOtherClassInjected: MyOtherClass) { (…) }
- @Singleton class MyClassThatShouldBeASingleton (…)
Basic usage
A typical singleton class :
import javax.inject._
@Singleton
class BurgersRepository {
// implementation goes here
}
Another class, requiring access to the first one.
import javax.inject._
class FastFoodService @Inject() (burgersRepository: BurgersRepository){
// implementation goes here
// burgersRepository can be used
}
Finally a controller using the last one. Note since we didn’t mark the FastFoodService as a singleton, a new instance of it is created each time it is injected.
import javax.inject._
import play.api.mvc._
@Singleton
class EatingController @Inject() (fastFoodService: FastFoodService) extends Controller {
// implementation goes here
// fastFoodService can be used
}
Injecting Play classes
You will often need to access instances of classes from the framework itself (like the WSClient, or the Configuration). You can inject them in your own classes :
class ComplexService @Inject()(
configuration: Configuration,
wsClient: WSClient,
applicationLifecycle: ApplicationLifecycle,
cacheApi: CacheApi,
actorSystem: ActorSystem,
executionContext: ExecutionContext
) {
// Implementation goes here
// you can use all the injected classes :
//
// configuration to read your .conf files
// wsClient to make HTTP requests
// applicationLifecycle to register stuff to do when the app shutdowns
// cacheApi to use a cache system
// actorSystem to use AKKA
// executionContext to work with Futures
}
Some, like the ExecutionContext, will likely more easy to use if they’re imported as implicit. Just add them in a second parameter list in the constructor :
class ComplexService @Inject()(
configuration: Configuration,
wsClient: WSClient
)(implicit executionContext: ExecutionContext) {
// Implementation goes here
// you can still use the injected classes
// and executionContext is imported as an implicit argument for the whole class
}
Defining custom bindings in a Module
Basic usage of dependency injection is done by the annotations. When you need to tweak things a little bit, you need custom code to further specify how you want some classes to be instantiated and injected. This code goes in what is called a Module.
import com.google.inject.AbstractModule
// Play will automatically use any class called `Module` that is in the root package
class Module extends AbstractModule {
override def configure() = {
// Here you can put your customisation code.
// The annotations are still used, but you can override or complete them.
// Bind a class to a manual instantiation of it
// i.e. the FunkService needs not to have any annotation, but can still
// be injected in other classes
bind(classOf[FunkService]).toInstance(new FunkService)
// Bind an interface to a class implementing it
// i.e. the DiscoService interface can be injected into another class
// the DiscoServiceImplementation is the concrete class that will
// be actually injected.
bind(classOf[DiscoService]).to(classOf[DiscoServiceImplementation])
// Bind a class to itself, but instantiates it when the application starts
// Useful to executes code on startup
bind(classOf[HouseMusicService]).asEagerSingleton()
}
}