Documentation

You are viewing the documentation for the 2.8.20 release in the 2.8.x series of releases. The latest stable release series is 3.0.x.

§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
import play.api.inject.Binding
import play.api.inject.Module

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 play.api.inject.ApplicationLifecycle
import scala.concurrent.Future

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 touch play.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.

Next: Reactive Streams integration (experimental)