我是Scala世界的新手,我正在使用PLAY制作API。它进展顺利,但我在理解一些符号方面遇到了一些麻烦,并且没有很多文档。具体来说,我对PLAY网站上的一个示例中的以下控制器方法感到困惑:
class HomeController @Inject()(@Named("userParentActor") userParentActor: ActorRef,
cc: ControllerComponents)
(implicit ec: ExecutionContext) {
}
我的问题是这个构造函数中发生了什么?哪个部分是构造函数,哪个部分是注入参数?注入了ExecutionContext
吗?为什么ec
在单独的括号中?
感谢您的澄清。
答案 0 :(得分:2)
它只是一个带有两个参数列表的构造函数,第二个列表中的参数是隐式的。
class HomeController @Inject()( // list-1 start
@Named("userParentActor") userParentActor: ActorRef, // list-1, arg-1
cc: ControllerComponents // list-1, arg-2
)( // 1 end, 2 start
implicit ec: ExecutionContext // list-2, arg-1
) { // list-2 end
// body
}
@Inject
注释适用于两个参数列表,因此ec
也由guice注入(使用Play's default thread pool)。
@Named
注释仅影响第一个参数。
ec
参数位于单独的列表中,因为必须在单独的列表中声明隐式参数。
它在一个单独的列表中声明可能是因为作者预期了控制器被手动实例化的用例而不是依赖注入容器:它更简单,因为你不必在任何地方指定默认的线程池。
答案 1 :(得分:1)
好的,让我们回过头来谈谈为什么Play演变成这样的。通常,当你想以命令式的方式编写类/方法/函数时,我们会编写类似的东西:
class XProviderFromCloud {
def getX (xId: String) : X = ??? // ???: To be implemented
}
假设上面的代码位于models
中的某个位置,这没关系,您可以导入模型并在此处使用该方法。不过一个好的工程
这里的方法是创建接口和测试事物:类似于测试驱动开发(TDD)。那么在这种情况下,代码将是:
trait XProvider{
def getX(xId: String): x
}
class XProviderFromCloud extends Xprovider{
override def getX (xId: String) : X = ??? // ???: To be implemented
}
您可以在此处查看界面,以便将界面注入控制器:
class MyController @inject()(xProvider: XProvider)
所以你可以在这里看到你的控制器类,并且要使用多个可注射组件。我这样做的主要原因之一,
是因为我可以模拟界面并返回结果;并测试。所以这意味着,我不需要在其中包含代码
override def getX
来测试执行此操作的控制器。在我确定控制器可以使用getX的结果后,我写了
测试getX
,然后编写它的主体代码。
现在让我们使用@Named
注释转到下一个点。有时一个接口有多个实现(扩展数量为
我们使用@Named注释来明确表达我们想要的实现。例如,我可以扩展上面的界面
有两个类从亚马逊云(例如S3)获取X,另一个从Google云获得它。就这么简单,你可以
另请查看文档:{{3}}
你可能会问ec: ExecutionContext
部分怎么样?好吧,这是后来当你想要处理并发和期货时。以上
代码,如果我们想要调用云服务或数据库,那么它并不是并发的;我们需要写一个非阻塞
并发代码,使用Future。期货在cpu theads上运行,我们可以使用默认执行上下文(如代码中所示),
或创建我们自己的执行上下文,如Play的文档中所示:https://www.playframework.com/documentation/2.6.x/ScalaDependencyInjection#Programmatic-bindings。