§Migrating Plugin to Module
Note: The deprecated
play.Plugin
system is removed as of 2.5.x.
If you have implemented a Play plugin, please consider migrating your implementation to use play.api.inject.Module
, instead of the deprecated Java play.Plugin
or Scala play.api.Plugin
types.
The main difference between the old Plugin
API, and the new one using Module
, is that with the latter we are going to fully embrace Dependency Injection (DI) - you can read here to understand why Play became opinionated about DI.
§Rationale
With the old Plugin
API, you were required to provide a play.plugins
file containing a number and the fully qualified name of the plugin to load. The number was used by Play to determine a total order between plugins. This approach was brittle, as it was difficult to ensure that the number corresponded to the right place in the initialization order, so the plugin’s dependencies were initialized before the plugin was initialized. Furthermore, there was no way to avoid that two plugins would use the same number. Both problems have been solved by using DI. Components now explicitly declare their dependencies as constructor arguments and do their initialization in the constructor.
§Create a Module
class
Start by creating a class that inherits from play.api.inject.Module
, and provide an implementation for the bindings
method. In this method you should wire types to concrete implementation so that components provided by your module can be injected in users’ code, or in other modules. Next follows an example.
In Java:
import play.api.Configuration;
import play.api.Environment;
import play.api.inject.Binding;
import play.api.inject.Module;
import scala.collection.Seq;
public class MyModule extends Module {
public Seq<Binding<?>> bindings(Environment environment, Configuration configuration) {
return seq(
bind(MyComponent.class).to(MyComponentImpl.class)
);
}
}
In Scala:
import play.api.Configuration
import play.api.Environment
class MyModule extends Module {
def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = {
Seq(
bind[MyComponent].to[MyComponentImpl]
)
}
}
Note that if a component you are defining requires another component, you should simply add the required component as a constructor’s dependency, prepending the constructor with the @javax.inject.Inject
annotation. The DI framework will then take care of the rest.
Note: if a component B requires A, then B will be initialized only after A is initialized.
Next follows an example of a component named MyComponentImpl
requiring the ApplicationLifecycle
component.
In Java:
import javax.inject.Inject;
import play.inject.ApplicationLifecycle;
import play.libs.F;
public interface MyComponent {}
class MyComponentImpl implements MyComponent {
@Inject
public MyComponentImpl(ApplicationLifecycle lifecycle) {
// previous contents of Plugin.onStart
lifecycle.addStopHook( () -> {
// previous contents of Plugin.onStop
return F.Promise.pure(null);
});
}
}
In Scala:
import javax.inject.Inject
import scala.concurrent.Future
import play.api.inject.ApplicationLifecycle
trait MyComponent
class MyComponentImpl @Inject() (lifecycle: ApplicationLifecycle) extends MyComponent {
// previous contents of Plugin.onStart
lifecycle.addStopHook { () =>
// previous contents of Plugin.onStop
Future.successful(())
}
}
§Wire it up
Now it’s time to add your freshly created Module
class to the set of enabled modules. Doing so is as simple as adding the following line in your configuration file:
play.modules.enabled += "my.module.MyModule"
If you are working on a library that will be used by other projects (including sub projects), add the above line in your reference.conf
file (if you don’t have a reference.conf
yet, create one and place it under src/main/resources
). Otherwise, if it’s in an end Play project, it should be in application.conf
.
If it’s and end Play project, you could also create a class called Module
and put it in the root package (the “app” directory).
Note: If you are working on a library, it is highly discouraged to use
play.modules.disabled
to disable modules, as it can lead to nondeterministic results when modules are loaded by the application (see this issue for reasons on why you should not touchplay.modules.disabled
). In fact,play.modules.disabled
is intended for end users to be able to override what modules are enabled by default.
§Compile-time DI
By defining a Module
class, you have made your component usable with runtime DI frameworks such a Google Guice or Spring. An alternative that is popular in Scala is compile-time DI. To make your component usable also with compile-time DI provide a Scala trait
that declares the component’s dependencies. Here is how you would do it for the running example:
import play.api.inject.ApplicationLifecycle
trait MyComponents {
def applicationLifecycle: ApplicationLifecycle
lazy val component: MyComponent = new MyComponentImpl(applicationLifecycle)
}
§Delete your Plugin
class and play.plugins
file
At this point, you should have successfully migrated your code, hence it’s time to delete both your Plugin
class and the play.plugins
file. The latter is typically located in your project’s conf
folder.
Found an error in this documentation? The source code for this page can be found here. After reading the documentation guidelines, please feel free to contribute a pull request. Have questions or advice to share? Go to our community forums to start a conversation with the community.