Documentation

You are viewing the documentation for the 2.4.x release series. The latest stable release series is 3.0.x.

§Dependency Injection

Dependency injection is a way that you can separate your components so that they are not directly dependent on each other, rather, they get injected into each other.

Out of the box, Play provides dependency injection support based on JSR 330. The default JSR 330 implementation that comes with Play is Guice, but other JSR 330 implementations can be plugged in.

§Declaring dependencies

If you have a component, such as a controller, and it requires some other components as dependencies, then this can be declared using the @Inject annotation. The @Inject annotation can be used on fields or on constructors, which you decide to use is up to you. For example, to use field injection:

import javax.inject.*;
import play.libs.ws.*;

public class MyComponent {
    @Inject WSClient ws;

    // ...
}

To use constructor injection:

import javax.inject.*;
import play.libs.ws.*;

public class MyComponent {
    private final WSClient ws;

    @Inject
    public MyComponent(WSClient ws) {
        this.ws = ws;
    }

    // ...
}

Each of these have their own benefits, and which is best is a matter of hot debate. For brevity, in the Play documentation, we use field injection, but in Play itself, we use constructor injection.

§Dependency injecting controllers

There are two ways to make Play use dependency injected controllers.

§Injected routes generator

By default, Play will generate a static router, that assumes that all actions are static methods. By configuring Play to use the injected routes generator, you can get Play to generate a router that will declare all the controllers that it routes to as dependencies, allowing your controllers to be dependency injected themselves.

We recommend always using the injected routes generator, the static routes generator exists primarily as a tool to aid migration so that existing projects don’t have to make all their controllers non static at once.

To enable the injected routes generator, add the following to your build settings in build.sbt:

routesGenerator := InjectedRoutesGenerator

When using the injected routes generator, prefixing the action with an @ symbol takes on a special meaning, it means instead of the controller being injected directly, a Provider of the controller will be injected. This allows, for example, prototype controllers, as well as an option for breaking cyclic dependencies.

§Injected actions

If using the static routes generator, you can indicate that an action has an injected controller by prefixing the action with @, like so:

GET        /some/path        @controllers.Application.index()

§Component lifecycle

The dependency injection system manages the lifecycle of injected components, creating them as needed and injecting them into other components. Here’s how component lifecycle works:

§Singletons

Sometimes you may have a component that holds some state, such as a cache, or a connection to an external resource, or a component might be expensive to create. In these cases it may be important that there is only one instance of that component. This can be achieved using the @Singleton annotation:

import javax.inject.*;

@Singleton
public class CurrentSharePrice {
    private volatile int price;

    public void set(int p) {
        price = p;
    }

    public int get() {
        return price;
    }
}

§Stopping/cleaning up

Some components may need to be cleaned up when Play shuts down, for example, to stop thread pools. Play provides an ApplicationLifecycle component that can be used to register hooks to stop your component when Play shuts down:

import javax.inject.*;
import play.inject.ApplicationLifecycle;
import play.libs.F;

import java.util.concurrent.Callable;

@Singleton
public class MessageQueueConnection {
    private final MessageQueue connection;

    @Inject
    public MessageQueueConnection(ApplicationLifecycle lifecycle) {
        connection = MessageQueue.connect();

        lifecycle.addStopHook(() -> {
            connection.stop();
            return F.Promise.pure(null);
        });
    }

    // ...
}

The ApplicationLifecycle will stop all components in reverse order from when they were created. This means any components that you depend on can still safely be used in your components stop hook, since because you depend on them, they must have been created before your component was, and therefore won’t be stopped until after your component is stopped.

Note: It’s very important to ensure that all components that register a stop hook are singletons. Any non singleton components that register stop hooks could potentially be a source of memory leaks, since a new stop hook will be registered each time the component is created.

§Providing custom bindings

It is considered good practice to define an interface for a component, and have other classes depend on that interface, rather than the implementation of the component. By doing that, you can inject different implementations, for example you inject a mock implementation when testing your application.

In this case, the DI system needs to know which implementation should be bound to that interface. The way we recommend that you declare this depends on whether you are writing a Play application as an end user of Play, or if you are writing library that other Play applications will consume.

§Play applications

We recommend that Play applications use whatever mechanism is provided by the DI framework that the application is using. Although Play does provide a binding API, this API is somewhat limited, and will not allow you to take full advantage of the power of the framework you’re using.

Since Play provides support for Guice out of the box, the examples below show how to provide bindings for Guice.

§Binding annotations

The simplest way to bind an implementation to an interface is to use the Guice @ImplementedBy annotation. For example:

import com.google.inject.ImplementedBy;

@ImplementedBy(EnglishHello.class)
public interface Hello {

    String sayHello(String name);
}
public class EnglishHello implements Hello {

    public String sayHello(String name) {
        return "Hello " + name;
    }
}

§Programmatic bindings

In some more complex situations, you may want to provide more complex bindings, such as when you have multiple implementations of the one trait, which are qualified by @Named annotations. In these cases, you can implement a custom Guice Module:

import com.google.inject.AbstractModule;
import com.google.inject.name.Names;

public class HelloModule extends AbstractModule {
    protected void configure() {

        bind(Hello.class)
                .annotatedWith(Names.named("en"))
                .to(EnglishHello.class);

        bind(Hello.class)
                .annotatedWith(Names.named("de"))
                .to(GermanHello.class);
    }
}

To register this module with Play, append it’s fully qualified class name to the play.modules.enabled list in application.conf:

play.modules.enabled += "modules.HelloModule"

§Configurable bindings

Sometimes you might want to read the Play Configuration or use a ClassLoader when you configure Guice bindings. You can get access to these objects by adding them to your module’s constructor.

In the example below, the Hello binding for each language is read from a configuration file. This allows new Hello bindings to be added by adding new settings in your application.conf file.

import com.google.inject.AbstractModule;
import com.google.inject.ConfigurationException;
import com.google.inject.name.Names;
import play.Configuration;
import play.Environment;

public class HelloModule extends AbstractModule {

    private final Environment environment;
    private final Configuration configuration;

    public HelloModule(
          Environment environment,
          Configuration configuration) {
        this.environment = environment;
        this.configuration = configuration;
    }

    protected void configure() {
        // Expect configuration like:
        // hello.en = "myapp.EnglishHello"
        // hello.de = "myapp.GermanHello"
        Configuration helloConf = configuration.getConfig("hello");
        // Iterate through all the languages and bind the
        // class associated with that language. Use Play's
        // ClassLoader to load the classes.
        for (String l: helloConf.subKeys()) {
            try {
                String bindingClassName = helloConf.getString(l);
                Class<? extends Hello> bindingClass =
                  environment.classLoader().loadClass(bindingClassName)
                  .asSubclass(Hello.class);
                bind(Hello.class)
                        .annotatedWith(Names.named(l))
                        .to(bindingClass);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Note: In most cases, if you need to access Configuration when you create a component, you should inject the Configuration object into the component itself or into the component’s Provider. Then you can read the Configuration when you create the component. You usually don’t need to read Configuration when you create the bindings for the component.

§Eager bindings

In the code above, new EnglishHello and GermanHello objects will be created each time they are used. If you only want to create these objects once, perhaps because they’re expensive to create, then you should use the @Singleton annotation as described above. If you want to create them once and also create them eagerly when the application starts up, rather than lazily when they are needed, then you can use Guice’s eager singleton binding.

import com.google.inject.AbstractModule;
import com.google.inject.name.Names;

public class HelloModule extends AbstractModule {
    protected void configure() {

        bind(Hello.class)
                .annotatedWith(Names.named("en"))
                .to(EnglishHello.class)
                .asEagerSingleton();

        bind(Hello.class)
                .annotatedWith(Names.named("de"))
                .to(GermanHello.class)
                .asEagerSingleton();
    }
}

§Play libraries

If you’re implementing a library for Play, then you probably want it to be DI framework agnostic, so that your library will work out of the box regardless of which DI framework is being used in an application. For this reason, Play provides a lightweight binding API for providing bindings in a DI framework agnostic way.

To provide bindings, implement a Module to return a sequence of the bindings that you want to provide. The Module trait also provides a DSL for building bindings:

import play.api.*;
import play.api.inject.*;
import scala.collection.Seq;

public class HelloModule extends Module {
    @Override
    public Seq<Binding<?>> bindings(Environment environment, Configuration configuration) {
        return seq(
            bind(Hello.class).qualifiedWith("en").to(EnglishHello.class),
            bind(Hello.class).qualifiedWith("de").to(GermanHello.class)
        );
    }
}

This module can be registered with Play automatically by appending it to the play.modules.enabled list in reference.conf:

play.modules.enabled += "com.example.HelloModule"

In order to maximise cross framework compatibility, keep in mind the following things:

§Excluding modules

If there is a module that you don’t want to be loaded, you can exclude it by appending it to the play.modules.disabled property in application.conf:

play.modules.disabled += "play.api.db.evolutions.EvolutionsModule"

§Advanced: Extending the GuiceApplicationLoader

Play’s runtime dependency injection is bootstrapped by the GuiceApplicationLoader class. This class loads all the modules, feeds the modules into Guice, then uses Guice to create the application. If you want to control how Guice initializes the application then you can extend the GuiceApplicationLoader class.

There are several methods you can override, but you’ll usually want to override the builder method. This method reads the ApplicationLoader.Context and creates a GuiceApplicationBuilder. Below you can see the standard implementation for builder, which you can change in any way you like. You can find out how to use the GuiceApplicationBuilder in the section about testing with Guice.

import play.Application;
import play.ApplicationLoader;
import play.Configuration;
import play.inject.guice.GuiceApplicationBuilder;
import play.inject.guice.GuiceApplicationLoader;
import play.libs.Scala;

public class CustomApplicationLoader extends GuiceApplicationLoader {

    @Override
    public GuiceApplicationBuilder builder(ApplicationLoader.Context context) {
        Configuration extra = new Configuration("a = 1");
        return initialBuilder
            .in(context.environment())
            .loadConfig(extra.withFallback(context.initialConfiguration()))
            .overrides(overrides(context));
    }

}

When you override the ApplicationLoader you need to tell Play. Add the following setting to your application.conf:

play.application.loader = "modules.CustomApplicationLoader"

You’re not limited to using Guice for dependency injection. By overriding the ApplicationLoader you can take control of how the application is initialized.

Next: HTTP architecture


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.