konquestor
konquestor

Reputation: 1308

@Inject object null: Play dependency injection

I instantiated using dependency injection in my controller class. When i try to use that object using @inject annotation elsewhere, i get null value. Here is my code skeleton.

@singleton
class ServiceClient(ws: WSClient, config: Configuration) {
 def get response() {...}
}

class App @Inject()(client: ServiceClient) extends Controller {

   def getItems = Action  {
     Obj()
   }
}

case class Obj() {

  @Inject
  var client: ServiceClient = _
  def doStuff() {
    client.getResponse() //client is null so get null pointer exception.
    ....
  }
}

The @Inject in Obj never seems to be working. and my client object is always null. (I do not want to pass client as param to Obj.) My expectation was that ServiceClient object created in controller should have been injected in Obj(). What did i do wrong?

update

Injecting Obj to the controller, is not an option. (In my app Obj are instantiated at run time my some complicated rule, using reflection).Also i dont want to have the constructor injection in the Obj. I am looking for field injection of ServiceClient outside of the controller.

update2

I was able to solve the problem using

var client: ServiceClient = play.api.Play.current.injector.instanceOf(classOf[ServiceClient]) 

in the Obj class. However, play.api.Play.current is deprecated in play 2.5 so i am still kind of stuck with a warning.

Upvotes: 0

Views: 1137

Answers (1)

yahor
yahor

Reputation: 401

You cannot inject dependency into the class instance without asking about it the DI framework. In your code you manually instantiate Obj which means the dependency injection magic is not happening. In order to inject dependency to Obj class its instantiation should be handled by the DI framework. What you can do here is:

  1. Inject ServiceClient to either Obj constructor or filed:

    class Obj @Inject() (client: ServiceClient) {
      //
    }
    

    It makes sense to use simple class instead of case class because case class sounds more like "data" class with immutable data inside rather than service or some helper class. Actually, your current definition of Obj class should work.

  2. Inject Obj and (or, depending on your needs) ServiceClient to your App class:

    class App @Inject()(obj: Obj, client: ServiceClient) extends Controller {
      def getItems = Action  {
        obj.doStuff()
      }
    }
    

I'd recommend you to read more about dependency injection and Guice framework which is used in Play by default. Guice wiki

Upvotes: 1

Related Questions