Scala PLAY Guice 注入和隐式

Scala PLAY Guice Injection and Implicits

我是 Scala 世界的新手,我正在使用 PLAY 制作 API。一切顺利,但我在理解某些符号时遇到了一些麻烦,并且没有很多文档。具体来说,我对 PLAY 网站上的一个示例中的以下控制器方法感到困惑:

class HomeController @Inject()(@Named("userParentActor") userParentActor: ActorRef,
                               cc: ControllerComponents)
                              (implicit ec: ExecutionContext) {
}

我的问题是这个构造函数中发生了什么?哪一部分是构造函数,哪一部分是注入参数? ExecutionContext 也注入了吗?为什么 ec 在单独的括号中?

感谢您的澄清。

它只是一个有两个参数列表的构造函数,第二个列表中的参数是隐式的。

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 参数在单独的列表中,因为隐式参数必须在单独的列表中声明。

它在单独的列表中声明可能是因为作者预期了控制器由依赖注入容器手动实例化的用例:这样更简单,因为您不必指定默认线程池无处不在。

好吧,让我们回过头来谈谈为什么 Play 会进化成这样。通常当你想以一种命令式的方式写一个 class/method/function 时,我们会这样写:

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)

所以你可以在这里看到,你的控制器 class,并且有许多可注射的组件可供使用。我这样做的主要原因之一, 是因为我可以模拟接口并返回 运行 结果;并对此进行测试。所以这意味着,我不需要里面有代码 override def getX 测试实现此功能的控制器。在确定控制器可以使用 getX 的结果后,我编写了 测试 getX,然后为其主体编写代码。

现在让我们转到下一点,使用 @Named 注释。有时一个接口有多个实现(扩展了 classes),我们使用@Named 注释来明确表达我们想要的实现。例如我可以扩展上面的接口 两个 classes 从亚马逊云(例如 S3)获取 X,另一个从 Google 云获取。就这么简单,你可以 另请查看文档:https://www.playframework.com/documentation/2.6.x/ScalaDependencyInjection#Programmatic-bindings

ec: ExecutionContext 部分呢,您可能会问。好吧,这是稍后你想要处理并发和 Futures 的时候。以上 如果我们想调用云服务或数据库,代码在某种意义上是不好的,因为它不是并发的;我们需要写一个 non-blocking 并发代码,使用 Future。 Futures 运行 on the cpu theads,我们可以使用默认的执行上下文(如代码中所示), 或者创建我们自己的执行上下文,如 Play 文档中所示:https://www.playframework.com/documentation/2.6.x/ScalaAsync#Creating-non-blocking-actions