我正在查看来自播放邮件的scaly代码示例:https://github.com/playframework/play-mailer
它基本上是这样的:
class MyComponent @Inject() (mailerClient: MailerClient) {
...
}
足够简单,编译时没有合规
然后我尝试"打电话"然而,它似乎并不是满足编译器或获取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
虽然我可能已经接近了,因为这个:
How does @Inject in Scala work
这表明通过从构造函数中删除@Inject
并将其放在字段上,以下语法可能会起作用。
@Inject var mailerClient: MailerClient = null
然而,当我们尝试运行任何需要该引用的东西时,我们仍然会得到null。
我正在阅读@Inject
上可以找到的所有内容([警告] [咆哮]我不是这样的编译器魔法的粉丝出于这个原因 - 伏都教魔法是美妙的,直到它停止工作然后似乎没有人知道如何修复它。[/ rant] [/警告])
但我真正想知道的是如何正确,安全和有效地使用它。
答案 0 :(得分:10)
由于您在原始GitHub仓库中关闭了您的问题,我不知道这个答案是否仍然是必要的,但由于您不完全了解DI框架的使用,我发现学习这项技能非常重要,我将尝试在此解释并列出一些好处。
首先,实例化实例的方式并没有给DI框架注入依赖关系的机会。由于new
是一种语言关键字,因此DI无法干预,并且无法注入您所需的类依赖关系。如何完成是通过构造函数或字段注入。我将主要关注构造函数注入,因为它是scala世界中的“标准”。
如果使用@Injected
注释指定构造函数参数,则基本上告诉DI框架从容器中解析此依赖关系。 DI框架在其容器内查找该对象的条目。如果它不存在,它将创建它(并在进程中解析其依赖关系),如果它用@Singleton
注释,也保存此实例以供将来使用。大多数DI框架要求您在大多数情况下指定起始类,但因为您正在使用Play!这个框架没有必要。如果要在控制器中使用特定模块,可以执行以下操作:
import javax.inject.Inject
import play.api.mvc.Controller
class Test @Inject() (val dependency: FooClass) extends Controller {
...
}
在这种情况下,FooClass
是要注入控制器的依赖项的类名。假设FooClass
将Play的Application
作为依赖关系,这将被注入,因为Play提供了一些预先绑定的预设,如Application
,但ActorSystem
。
这是可能的,因为玩!框架使用DependencyInjectedRoutes
。如果您要在Controller之外创建一个Actor,则需要在模块类中指定它,但在link和此link中对此进行了解释。
还有一个概念是在你的控制器中使用Traits
,然后将这些特性与实现类连接起来,但我认为现在有点太复杂了。
如果您想要使用这种编写应用程序的方法获得一些好处和成功案例,这里有一个很好的资源:https://softwareengineering.stackexchange.com/a/19204/164366
如果你想要阅读这个概念:
我希望这可以解决问题!如果您有疑问,请询问!
答案 1 :(得分:0)
这不是scala问题,而是DI问题。您应该阅读一些guice文档。
在Play 2.4.x中,您需要使用依赖注入(https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection) 实现你的目标。
你的AnObject应该是:
@Singleton class AnObject @Inject()(mailer:MyComponent){
def sendEmail = mailer.doStuff
}
答案 2 :(得分:0)
我遇到了同样的问题。我想创建一个具有邮件功能的类或对象,然后每当我想从任何控制器发送电子邮件时我都可以调用它。
我认为你所要求的相当于如何使用mailerclient外部游戏框架。据我所知,你做不到。即使你在app / controllers文件夹中创建了一个类,它也只是一个常规的scala类,与play框架中的魔法无关。 @Inject仅适用于控制器或模块。因为如果你创建一个独立的类,你必须在实例化它时自己注入一些东西。除非您正在构建模块或扩展控制器。您是否注意到AppController曾经是对象,现在它是一个类?您在使用它时不需要实例化它。我相信框架做了一些事情,用配置(注入)来实例化它。
因此,如果您想实现原始设计目标,您可以编写模块,发布模块,然后在所有控制器中使用它;或者使用scala中的其他一些电子邮件库来完成电子邮件发送功能。
答案 3 :(得分:0)
(我没有足够的声誉发表评论,所以发帖作为答案)
@aparo发布的答案应标记为核心/已批准的答案,因为它确实解决了问题。你说这不能解决原来的问题,因为它将依赖关系移动到另一个类,但这只是部分正确,因为其他类只需要为你提供MyComponent
而不是{{ 1}}。尽管依赖注入需要一直使用到最终控制器(在Play的情况下),但通常不需要注入更多的单个对象。
可以看到in a question I posted的证据,因为我当时的心态与你一样。在我的示例中,我的控制器只需要一个MailerClient
依赖项,UserSearch
依赖项由guice处理,所以我不需要再在任何地方声明它。