How to access Play Framework 2.4 guice Injector in application?
Asked Answered
B

5

9

I want to use the getInstance method of the Guice Injector class in Play Framework 2.4, how can I access it?

I have used the Guice FactoryModuleBuilder for implementing a factory that returns another factory at runtime! At the second level of returning a factory I need to access the Play Guice Injector to get objects manually using reflection instead of @Inject annotation.

Balenciaga answered 19/1, 2016 at 5:57 Comment(5)
Could you please elaborate more here? Why do you need to access the injector?Debose
@Debose I have used Guice FactoryModuleBuilder for implementing a factory that return another factory in runtime! in second level of returning factory i need to access to Play guice Injector to get objects manually using reflection instead of Inject annotation.Balenciaga
I asked because you should avoid using Play.current since it will be removed at some point. Maybe you can inject the injector?Debose
@Debose How can I inject the injector? I think it is a better solution. Thanks.Balenciaga
Just like anything else. See an example (Java) here: github.com/edulify/play-sitemap-module.edulify.com/blob/master/…Debose
A
16

There are many ways. I use this one.

Edit: This is relevant for Play versions which are <= 2.4:

Play.maybeApplication.map(_.injector.instanceOf[MyProdClass]).getOrElse(new MyDevClass)

or

Play.current.injector.instanceOf[MyClass]

For versions which are >= 2.5:

import play.api.inject.Injector
import javax.inject.Inject

class MyService @Inject() (injector: Injector) ={
    val myClassInstance = injector.instanceOf[MyClass]
    //do stuff
}
Aseptic answered 19/1, 2016 at 6:23 Comment(5)
While technically correct, the first approach would lead to the wrong instance if the Injector is accessed statically (in a productive system). This shouldn't happen - of course, but the second approach would at least be clear about it. You should use another application loader in the dev environment if you want to have different bindings.Ossetia
Play.current is deprecated as all other static accesors are. DI must be used instead of old static approach.Winola
How does the first approach work? How is this application created? I am trying to introduce Guice Injection in a Play 2.3 application, where the injector is defined inside Global.scala? This works, but I can't find a decent way to access the injector without the @Inject notation elsewhere.Scuff
@Scuff you can have a check of my answer to this question, based on Devabc's solution.Prepossession
While the question is about Guice injector, the accepted answer is about Play injector, which is a different class.Bookerbookie
L
8

With Play Framework 2.5.x play.api.Play.current is deprecated and DI should always be preferred. Therefore proper way of getting injector instance is by using:

import play.api.inject.Injector
import javax.inject.Inject

class SomeClassRequiringInjector @Inject() (injector: Injector) { ... }

Works for me even when doing this with DI Provider class :)

Laris answered 6/7, 2016 at 21:15 Comment(3)
in my Java this type of injecting does not work. Any idea?Sensitive
@SimonFakir in Java you should inject play.inject.Injector, because everything under play.api.* is for Scala.Winola
@Egregore, I think your answer should be accepted! Also, it would be nice if you'll also include Java example for which play.inject.Injector must be injected.Winola
B
5

It is better to inject Guice Injector:

@Inject
private Injector injector;

Or you can use this code for Play Framework Java edition:

Play.application().injector().instanceOf(YourClass.class);
Balenciaga answered 20/1, 2016 at 12:24 Comment(0)
S
5

Apart from the other Play 2.5.x solutions, there may be a situation in which you want to get the Injector in an object without using injection. For example when you're using a Scala singleton, for which @inject probably doesn't work.

In that case, you can use this:

@Singleton
class GlobalContext @Inject()(injector: Injector) {
  GlobalContext.injector = injector
}

object GlobalContext {
  private var injector: Injector = null  
}

With a Guice module to set the singleton to eager, otherwise it won't be initialized automatically:

// Module.scala
class Module extends AbstractModule {
  override def configure() = {
    // ...

    // Eager initialize Context singleton
    bind(classOf[GlobalContext]).asEagerSingleton()
  }
}
Sensation answered 12/3, 2017 at 22:8 Comment(1)
Even if you binding as eager singleton, there are no guarantees that the injector will be available when you try to use itCulliton
P
3

I really like Devabc's solution because there definitely are some scenarios that can't rely on constructor injection to get the injector itself, however in Play 2.5.x you have to use the the deprecated play.api.Play.current.Injector code to get the injectorinstance.

His solution create a reference to the Play build-in injector and put it into a Scala object which can be imported by any components when they need it. Brilliant!

In order to make it work, however, the object needs to provide a public interface to get the injector, so here is my amended code to fix it and demo of how it can be used.

# GolbalContext.scala

import play.api.inject.Injector
import javax.inject.Inject

@Singleton
class GlobalContext @Inject()(playBuiltinInjector: Injector) {
  GlobalContext.injectorRef = playBuiltinInjector
}

object GlobalContext {
  private var injectorRef: Injector = _

  def injector: Injector = injectorRef
}

The initialization part is the same.

# InjectionModule.scala

package modules

class InjectionModule extends AbstractModule {
  override def configure() = {
    // ...

    // Eager initialize Context singleton
    bind(classOf[GlobalContext]).asEagerSingleton()
  }
}

Then in the application.conf, make sure the InjectionModule is the first module being enabled so the following modules can use the injector properly.

play.modules.enabled += "modules.InjectionModule"

And the client code is quite simple. In whichever component requires the injector:

// Import the object GlobalContext
import GlobalContext.injector

// ...
val yourClassInstance = injector.instanceOf[YourClass]
Prepossession answered 28/5, 2017 at 16:57 Comment(2)
This approach can fail when you require the injector during an earlier fase, prior to the injector initialisation (I'm facing this problem right now)Culliton
@ZéByte It should work by putting bind(classOf[GlobalContext]).asEagerSingleton() as first line of the Module and play.modules.enabled += "modules.InjectionModule" as the first Module being enabled. Since it would be the earliest module and any module afterward can access it.Prepossession

© 2022 - 2024 — McMap. All rights reserved.