If you are unfamiliar with the Play! Framework, it’s an MVC framework written in Scala and definitely worth checking out, especially if you’re a Java or Scala developer. One constraint of the framework that I have never really been fond of was the requirement for controllers to require static
methods for routing purposes.
What is wrong with static methods? Two disadvantages come to mind:
- Testability
- Extensibility
One of the most common solutions for these constraints is the use of dependency injection, or DI. As of release of version 2.4, the Play Framework now provides built in support for DI using Guice. I had previously only used the Spring Framework, but was fortunate enough to attend a talk a few years back where Bob Lee pointed out some disadvantages of Spring and introduced Guice as an alternative. With the introduction of JSR-330 and the collection of @Inject
annotations, it is fairly trivial to swap between frameworks.
Controllers
Although developers can continue to use controllers with static methods, it is much more advantageous and more of a best practice to use dependency injection instead. So, how do you define a controller to make use of the framework’s dependency injection? You define a POJO.
package controllers;
import play.mvc.*;
public class AdminController extends Controller {
public Result log() {
return TODO;
}
}
Routes
When declaring a route in the routes
file, the controller must be prefixed with an @
to indicate that Play should use an instance of the controller instantiated through the framework and invoke its instance method rather than a static
method.
# <METHOD> <PATH> <CONTROLLER>
GET /admin/log @controllers.AdminController.log()
Services
Being that controllers should be lightweight, the business logic should take place within services. Services can easily be injected into controllers using the @Inject
annotation.
Service
The service should be defined with the @Singleton
annotation attached to the class.
@Singleton
public class LogService {
public LogService() {
...
}
...
}
Controller
The controller can be rewritten to use the service via constructor dependency injection.
public class AdminController extends Controller {
private final LogService logService;
@Inject
public AdminController(LogService logService) {
this.logService = logService;
}
}
For basic dependency injection, that’s it! No need to import any additional dependencies, no XML files, and no ApplicationContext
.