Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala (Play 2.4.x) How to call a class with @inject() annotation

I'm looking at the scaly code example from play-mailer: https://github.com/playframework/play-mailer

It goes basically like this:

class MyComponent @Inject() (mailerClient: MailerClient) {
   ...
}

simple enough and it compiles without compliant

Then I try to "call" it however and there doesn't appear to be a way to satisfy the compiler OR get a working instance of mailerClient.

object AnObject {
  val mailer = new MyComponent
  def sendEmail = mailer.doStuff
}

[info] Compiling 1 Scala source to ...
[error] /SomeOne/SomePath/SomeFile.scala:30: not enough arguments for constructor MyComponent: (mailerClient: play.api.libs.mailer.MailerClient) MyComponent.
[error] Unspecified value parameter mailerClient.
[error]   val mailer = new MyComponent
[error]                ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed

I though I might have gotten close thanks to this:

How does @Inject in Scala work

Which indicated that the following syntax might work by removing the @Inject from the constructor and placing it on a field.

@Inject var mailerClient: MailerClient = null

However the moment we try to run anything that needs that reference we still get null.

I'm reading everything I can find on @Inject

( [warning] [rant] I'm NOT a fan of compiler magic like this for this exact reason -- voodoo magic is wonderful until it stops working then no one seems to have any idea of how to fix it. [/rant] [/warning] )

but what I really want to know is how to use it properly, safely and effectively.

like image 287
Techmag Avatar asked Jul 10 '15 14:07

Techmag


People also ask

What is the use of @inject annotation?

Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments. @Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.

What is inject in play framework?

Dependency injection is a widely used design pattern that helps to separate your components' behaviour from dependency resolution. Components declare their dependencies, usually as constructor parameters, and a dependency injection framework helps you wire together those components so you don't have to do so manually.

What is the dependency injection?

In object-oriented programming (OOP) software design, dependency injection (DI) is the process of supplying a resource that a given piece of code requires. The required resource, which is often a component of the application itself, is called a dependency.


1 Answers

Since you closed your issue on the original GitHub repo, I don't know if this answer is still necessary but since you don't fully understand the use of a DI framework and I find it incredibly important to learn this skill, I'll try to explain it here and list some benefits.

First off, the way you are instantiating your instance doesn't give the DI framework a chance to inject your dependencies. Since new is a language keyword, DI can't interfere and the dependencies you need for your class can't be injected. How it is done is through constructor or field injection. I'll mainly focus on constructor injection because that is "standard" in the scala world.

If you specify a constructor argument with the @Injected annotation, you are basically telling the DI framework to resolve this dependency from the container. The DI framework goes and looks for an entry of that object inside its container. If it doesn't exists, it will create it (and resolve its dependencies in the process) and if it's annotated with @Singleton also save this instance for future use. Most DI frameworks require you to specify a starting class in most cases but because you are using Play! Framework this is not necessary. When you want to use a particular module inside your controller you can do this:

import javax.inject.Inject

import play.api.mvc.Controller

class Test @Inject() (val dependency: FooClass) extends Controller {
  ...
}

In this case FooClass is the class name of the dependency you want to inject into your controller. Let's say FooClass has Play's Application as a dependency this will be injected, because Play provides a couple pre-bonded presets like Application but also ActorSystem.

This is possible because Play! Framework uses DependencyInjectedRoutes. If you were to create an Actor outside of an Controller you would need to specify that inside a module class but that is explained in this link and this link.

There is also a concept of using Traits inside your controller and then later on wiring together the traits with the implementation classes but I think that is a bit too complicated for now.

If you want some benefits and succes stories to this method of writing applications, here is a good resource: https://softwareengineering.stackexchange.com/a/19204/164366

If you want something to read on this concept:

  • https://www.playframework.com/documentation/2.4.x/ScalaAkka
  • https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
  • https://www.playframework.com/documentation/2.4.x/ScalaCompileTimeDependencyInjection

I hope this clears things up! If you have question, please do ask!

like image 177
Martijn Avatar answered Oct 10 '22 20:10

Martijn