In the previous article, we created our first application, with a generated routing file and a single controller with a single action.
As the project will grow, we will create additional controllers, each of them having various sets of dependencies. We will extract logic to services, DAO classes and utility classes, each of them having their own dependencies. As such, it is recommended to use a Dependency Injection pattern. Play framework comes out of box with DI provided by Guice
Guice application loader
Guice needs to be enabled explicitly:
Next, you can remove the
play.application.loader property from
application.conf. And you can now delete our custom application loader completely.
How does Play know now, what loader to use ?
SBT plugin searches classpath for files named “reference.conf”: https://www.playframework.com/documentation/2.6.x/ConfigFile
Guice library contains a file “reference.conf” with following contents:
play.application.loader = "play.api.inject.guice.GuiceApplicationLoader"
Settings from “reference.conf” are overridden by settings from “application.conf”, that is why we left that file empty.
Guice now does all he heavy lifting for us. It constructs the application by utilizing
play.api.inject.BuiltinModule and it injects our controller to the generated router. However, we need to modify our controller a bit. Most importantly, we need to annotate the constructor of the class (not the class itself) with the
And while doing so, we improved the action method a bit. We add a dependency on a bunch of components at once, grouped into play.api.mvc.ControllerComponents. This gives you access to the Action factory method, which is a more idiomatic way of how to define actions. I recommend to compare how the controller changed from the last time, while still doing the same work.
Macwire dependency injection
Guice is an example of runtime dependency injection. Dependencies are resolved after application has already started. There is danger of misconfiguration, which manifest as runtime bugs.
There is a possibility to detect unsatisfied dependencies during compile time, without having to code the wiring manually. The answer are Scala macros and a library called MacWire
Let’s add a new dependency:
The “wire” macro will save us from tedious manual construction of Router and all it’s controllers.
And that’s basically it! It might feel a bit weird now. Most of us have been using sophisticated DI frameworks like Spring for years. And now somebody claims, that you can achieve the same simply by using a single macro function?
The answer is no, of course. First of all, Spring’s DI is only one of it’s features. Spring also comes with a great number of useful modules, e.g. Data, Web, Batch. And you can still use these modules in Scala and Play too.
Macwire also does not promote any pattern on how to organize your beans. Moreover, it comes with restrictions right away. It only supports constructor injection. This implies, that circular dependencies simply are not possible. If you have existing beans, which only expose setters, that you have to create these beans either manually, or use Spring DI for set of beans, which are not MacWire compatible. Here is an example of these beans:
And than, you still need to decide, how you will organize your beans. This article gives a nice overview of possible solutions. Right away, it promotes so called “thin cake pattern” and that is, what we are going to use.
In the next post, we will split our application into multiple sub-modules.