Neilos
Neilos

Reputation: 2746

Play Framework 2.4 - Dependency injection to replace GlobalSettings.onStart()

I'm confused with regards to dependency injection. What I want to achieve is to replace the GlobalSettings.onStart() call, where I initialized some static singleton objects in 2.3, with proper dependency injection of those objects.

What I am trying to do is this:

Controller -> Model (inject an object into this model)

What I have so far is a half way measure; in the controller:

private static SomeObject myStaticSingletonObject = new SomeObject();

public Promise<Result> getSomeData() {
    return handleRequest(() -> new SomeDataAjaxRequest(myStaticSingletonObject));
}

public Promise<Result> handleRequest(Function0<AbstractAjaxRequest<?>> supplier) {
    Promise<AbstractAjaxRequest<?>> promise = Promise.promise(supplier);
    return promise.map(arg -> ok(arg.getResponse()));
}

handleRequest() is a custom method I use and isn't really related but I include it for completeness:

And in the model I just take the SomeObject as a param:

private final SomeObject someObject;

public SomeDataAjaxRequest(SomeObject someObject) {
    super(null);
    this.someObject = someObject;
}

In my build.sbt I have:

routesGenerator := InjectedRoutesGenerator

So basically my question is how should I be injecting SomeObject into my models and also how should I be creating my SomeObject object, I don't think I should be using new SomeObject().

Ideally I would like to use field injection for these objects as I don't want to clutter the constructor which might actually have relevant params for the model rather than just these utility classes that contain definitions of things (SomeObject basically just loads some information from the database which currently is static throughout the lifetime of the application, but that may change).

Also it is probably worth noting that I am intending to use Guice to manage DI.

I understand that I should create a Guice DI factory and have seen the docs for this but I still am unsure how to integrate this into my play app.

Upvotes: 3

Views: 1483

Answers (1)

Steve Chaloner
Steve Chaloner

Reputation: 8202

You don't need to explicitly create a Guice DI factory for this.

Instead, create a module and use this to configure any bindings and - importantly for you - replacements for onStart().

import play.api.inject.Module;

public class SomeModule extends Module {
    @Override
    public Seq<Binding<?>> bindings(final Environment environment,
                                    final Configuration configuration)
    {
        return seq(bind(SomeObject.class).toSelf().eagerly());
    }
}

Make sure you annotate SomeObject with javax.inject.Singleton to ensure its status. The point of eagerly in the module is to ensure the object is initialised as early as possible.

SomeObject will then be available for DI, having been invoked once; you can use the constructor to initialise whatever you need to do.

To expose this module to your application, add it to the application.conf:

play {
  modules {
    enabled += "com.example.SomeModule"
  }
}

In your controller, just inject the instance as you normally would:

public class SomeController extends Controller {
    private final SomeObject someObject;

    @Inject
    public SomeController(final SomeObject someObject) {
        this.someObject = someObject;
    }

    public Promise<Result> getSomeData() {
        return handleRequest(() -> new SomeDataAjaxRequest(someObject));
    }

    public Promise<Result> handleRequest(Function0<AbstractAjaxRequest<?>> supplier) {
        return Promise.promise(supplier)
                      .map(arg -> ok(arg.getResponse()));
    }
}

Upvotes: 2

Related Questions