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

I am looking at an example of scaly code from play-mailer: https://github.com/playframework/play-mailer

This mainly happens:

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

quite simple and compiles without matching

Then I try to β€œcall” it, but there seems to be no 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 

Although I may be very grateful for this:

How @Inject works in Scala

Which indicated that the following syntax could work by removing @Inject from the constructor and putting it in a field.

 @Inject var mailerClient: MailerClient = null 

However, the moment we try to run everything that needs this link, we still get null.

I read everything I can find on @Inject

([warning] [rant] I am not a fan of compiler magic, as it is for this exact reason - voodoo magic is wonderful until it stops working, then no one seems to have a clue how to fix it. [/ rant] [/ warning])

but I really want to know how to use it safely and effectively.

+7
scala annotations playframework inject
source share
4 answers

Since you covered your problem in the original GitHub repo, I don’t know if this answer is needed, but since you do not fully understand the use of DI frameworks, and I consider it incredibly important to learn this skill, I will try to explain it here and list some advantages.

First, the way you instantiate an instance does not give the DI framework the ability to inject your dependencies. Since new is a language keyword, DI cannot intervene, and the dependencies you need for your class cannot be introduced. How this is done through a constructor or field injection. I will mainly focus on constructor injections because this is the "standard" in the scala world.

If you specify a constructor argument with the @Injected annotation, you are basically talking about a DI structure to resolve this dependency from the container. The DI frame goes and looks for a record of this object inside its container. If it does not exist, it will create it (and resolve its dependencies in the process), and if it is annotated using @Singleton , also save this instance for future use. In most DI infrastructures, you need to specify a start class in most cases, but because you use Play! The framework is not necessary. If you want to use a specific 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 name of the class that you want to enter into the controller. Let's say FooClass has a Play Application as a dependency that will be introduced because Play provides a couple of pre-bonded presets like Application , as well as ActorSystem .

This is possible because Play! Framework uses DependencyInjectedRoutes . If you were to create an Actor outside of Control, you will need to specify this inside the module class, but this is explained in this link and this is the link .

There is also the concept of using Traits inside your controller, and then later wiring along with features with implementation classes, but I think now it is too complicated.

If you need some advantages and successful stories for this method of writing applications, here is a good resource: https://softwareengineering.stackexchange.com/a/19204/164366

If you want to read something on this concept:

Hope this clears up! If you have a question, please ask!

+10
source share

this is not a scala problem, but a DI. You should read the guice documentation.

In Play 2.4.x, you need to use dependency injection ( https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection ) to achieve your goal.

Your AnObject should be:

 @Singleton class AnObject @Inject()(mailer:MyComponent){ def sendEmail = mailer.doStuff } 
0
source share

I ran into the same problem. I want to create a class or object that has a distribution function, and then I can call it when I want to send emails from any controllers.

I think what you requested is equivalent to how to use the external mailerclient boot environment. As far as I understand, you can’t. Even if you create a class in the app / controller folder, this is a regular regular scala class that has nothing to do with magic in a game environment. @Inject only works with controllers or modules. Because if you create an autonomous class, you need to enter something yourself when you create it. If you do not create a module or expand the controller. Did you notice that the AppController used to be an object, and now it's a class? You do not need to instantiate when you use it. I believe that the structure did something to create an instance with the configuration (injected).

So, if you want to achieve the original design goal, you either write a module, publish it, and then use it in all controllers; or use some other email libraries in scala to wrap email sending functionality.

0
source share

(I do not have enough reputation to comment, so I am sending as an answer)

The response posted by @aparo should be marked as corect / approved, because it solves the problem. You said that this does not solve the original question, because it moves the dependency to another class, but this is only partially true, since this other class will need to be provided with MyComponent instead of MailerClient . Although dependency injection should be used up to the final controller (in case of playback), you usually do not need to enter more than one separate object.

Proof of this can be seen in the question that I posted , because I had the same thinking as you at that time. In my example, my controller only needs the UserSearch dependency, the DatabaseConfigurationProvider dependency is handled with guice, so I no longer need to specify it anywhere else.

0
source share

All Articles