Akka Play guice 绑定无标记最终 (TF) 支持
Akka Play guice bindings tagless final (TF) support
我有这个绑定来在我的应用程序中配置 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 的运行方式有什么不同吗?
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 一起工作,但我没有成功。
我有这个绑定来在我的应用程序中配置 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 的运行方式有什么不同吗?
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 一起工作,但我没有成功。