【问题标题】:Akka Play guice bindings tagless final (TF) supportAkka Play guice 绑定无标签最终(TF)支持
【发布时间】:2021-08-22 08:30:16
【问题描述】:

我有这个绑定来在我的应用程序中配置 Logger[IO](此行的模块在 guice.conf 文件中):


class CatsEffectModule extends AbstractModule with ScalaModule {

  override def configure(): Unit = {
    bind[Logger[IO]].toInstance(Slf4jLogger.getLogger[IO])
  }

}

然后在应用程序中我可以这样做:

@Singleton
class MyClass @Inject()(implicit logger: Logger[IO]) { ... }

这在应用程序中运行良好。

但在GuiceInjectorBuilder(用于测试)中使用时不起作用:

import play.api.inject.guice.GuiceInjectorBuilder


private val application: Injector = new GuiceInjectorBuilder()
    .bindings(bind[ExecutionContext].to(ExecutionContext.global))
    .bindings(bind[ApplicationLifecycle].to[DefaultApplicationLifecycle])
    .bindings(new CatsEffectModule())
    .build()

application.instanceOf[MyClass]

它给了我一个错误:

No implementation for io.chrisdavenport.log4cats.Logger was bound.
[info]   Did you mean?
[info]     io.chrisdavenport.log4cats.Logger<cats.effect.IO> bound  at guice.CatsEffectModule.configure(CatsEffectModule.scala:21) (via modules: com.google.inject.util.Modules$OverrideModule -> guice.CatsEffectModule)

测试中的每个 TF 实体注入都会像这样失败。 Akka Play 运行 Guice 的方式与 GuiceInjectorBuilder 的工作方式有什么区别吗?

代码示例:https://github.com/DenisNovac/play-tf-test

【问题讨论】:

  • 我想你可以试试这个 application.injector.instanceOf[CatsEffectModule]
  • @ShwetaValunj 它已经是一个注射器。 build() 只是在斗篷下打电话给injector()。另外我想我不需要 CatsEffectModule 的实例。它只包含一些其他绑定。即使我在测试中复制它们也行不通(我试过)。
  • 啊。我刚注意到建设者。您是否尝试过使用 org.scalatestplus.play.guice 中的 GuiceOneAppPerSuite ?
  • 据我所知,它使用GuiceApplicationBuilderguice.conf 启动所有模块。我使用GuiceInjectorBuilder 因为我只想要一个模块。也许它会起作用(因为它会遵循与启动应用程序相同的逻辑),但我需要让它与 GuiceInjectorBuilder 一起工作。
  • 我在问题中添加了示例 repo url。重写后,我注意到我们使用了一些自定义的AbstractModule 包装器 - ScalaModule。但在构建 guice builder 时,它仍然用于测试。

标签: scala akka guice tagless-final


【解决方案1】:

codingwell (https://github.com/codingwell/scala-guice) 的 Scala-guice 允许绑定 TF 类(它是为 vanilla Guice 制作的,而不是 Play Guice)。它们将正确绑定到依赖类,但GuiceInjectorBuilder 不会让您通过instanceOf 方法获取它。

但如果你同时使用两个绑定,它似乎在两个方向都有效:

import cats.effect.IO
import com.google.inject.AbstractModule
import net.codingwell.scalaguice.ScalaModule
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import play.api.inject.guice.GuiceInjectorBuilder
import play.api.inject.{ApplicationLifecycle, DefaultApplicationLifecycle, Injector, bind}

import scala.concurrent.ExecutionContext

class WorkingModule extends AbstractModule with ScalaModule {

  override def configure(): Unit =
    bind[CustomTFInterface[IO]].toInstance(new CustomTFInterfaceImpl)
}

class HomeControllerSpec extends AnyFlatSpec with Matchers {

  it should "test1" in {
    val application: Injector = new GuiceInjectorBuilder()
      .bindings(bind[ExecutionContext].to(ExecutionContext.global))
      .bindings(bind[ApplicationLifecycle].to[DefaultApplicationLifecycle])
      .bindings(bind[CustomTFInterface[IO]].toInstance(new CustomTFInterfaceImpl))
      .bindings(new CatsEffectModule())
      .injector()

    application.instanceOf[CustomTFInterface[IO]] // works
    //application.instanceOf[InjecableWithTfDependencies] // fails

  }

  it should "test2" in {
    val application: Injector = new GuiceInjectorBuilder()
      .bindings(bind[ExecutionContext].to(ExecutionContext.global))
      .bindings(bind[ApplicationLifecycle].to[DefaultApplicationLifecycle])
      .bindings(new WorkingModule())
      .bindings(new CatsEffectModule())
      .injector()

    //application.instanceOf[CustomTFInterface[IO]] // fails
    application.instanceOf[InjecableWithTfDependencies] // works
  }

  it should "test3" in {
    val application: Injector = new GuiceInjectorBuilder()
      .bindings(bind[ExecutionContext].to(ExecutionContext.global))
      .bindings(bind[ApplicationLifecycle].to[DefaultApplicationLifecycle])
      // both binds together also works
      .bindings(new WorkingModule())
      .bindings(bind[CustomTFInterface[IO]].toInstance(new CustomTFInterfaceImpl))
      .bindings(new CatsEffectModule())
      .injector()

    application.instanceOf[CustomTFInterface[IO]]       // works
    application.instanceOf[InjecableWithTfDependencies] // works
  }

}

也许有一种方法可以让 scala-guice 和 play 一起工作,但我没有运气。

完整示例:https://github.com/DenisNovac/play-tf-test

【讨论】:

    猜你喜欢
    • 2014-09-01
    • 1970-01-01
    • 2016-02-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多