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

前端 未结 4 1908
臣服心动
臣服心动 2021-02-06 05:29

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

It goes basically like this:

class MyComponent @         


        
相关标签:
4条回答
  • 2021-02-06 05:47

    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!

    0 讨论(0)
  • 2021-02-06 05:48

    this is not a scala Issues, but a DI one. You should read some 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 讨论(0)
  • 2021-02-06 05:56

    I ran into the same problem. I want to create a class or object that has the mailing functionality and then I can call it whenever I want to send out emails from any controllers.

    I think what you were asking is equivalent to how to use the mailerclient outside play framework. So far as I understand, you cannot. Even you create a class in the app/controllers folder, it is just a regular scala class that has nothing to do with the magics in play framework. @Inject works only with controllers or modules. Because if you create a standalone class, you have to inject something by yourself when instantiate it. Unless you are building a module or extending a controller. Do you noticed that the AppController used to be object and now it is a class? You don't need to instantiate that when you use it. I believe the framework did something to instantiate it with 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 up the email sending functionality.

    0 讨论(0)
  • 2021-02-06 05:56

    ( I don't have enough reputation to comment, so posting as an answer)

    The answer posted by @aparo should be marked as the corect/approved answer, because it does solve the problem. You said that this doesn't solve the original question because it moves the dependency down to another class, but this is only partially true, as that other class will only need to provide you with a MyComponent instead of a MailerClient. Although dependency injection needs to get used all the way down to the final controller (In the case of Play), you usually won't have to inject more one single object.

    Proof of this can be seen in a question I posted, as I had the same mindset as you at the time. In my example, my controller needs only a UserSearch dependency, the DatabaseConfigurationProvider dependency is dealt with by guice, so I don't need to state it anywhere again.

    0 讨论(0)
提交回复
热议问题