创建一个 class 的实例,它在 Scala 中通过 Playframework Guice 独立进行 DI

Create an Instance of class which does DI via Playframework Guice Independently in Scala

我正在开发 Playframework2.5,其中包含 play-slick 和与之相关的程序,例如批处理。

当前项目结构如下

/rootPlayProject
  /app
    /controllers
    /filters
    /services
    ...
  Modules
  /core (sub-project - DAOs,services are placed here)
  /batch (sub-project depends on core)

我几乎在任何地方都使用 Guice DI,包括数据库访问对象 (DAO)。 核心中的接口绑定在 Module 中,放置在 core 中,最终被根项目中的 Module 继承。

核心模块(/rootPlayProject/core/CoreModule.scala)

class CoreModule extends AbstractModule {
  override def configure() = {
    bind(classOf[FooDAO]).to(classOf[FooDAOImpl])
    ....
  }
}

根模块(/rootPlayProject/Modules.scala)

class Module extends CoreModule {
  override def configure() = {
    super.configure()
    bind(classOf[FooService]).to(classOf[FooServiceImpl])
  }
}

不过,这作为 Playframework 应用程序工作得很好,我想将核心模块用于批处理程序,并且我想 运行 没有 playframework 的批处理。

到目前为止我试过这样的东西

object BatchA {
  def main(args: Array[String]) = {
    val injector = Guice.createInjector(new CoreModule)
    val foo = injector.getInstance(classOf[FooDAO])
    //do something with foo.
  }
}

但是由于我的 DAO 需要 Playframework 创建的东西,例如 ExecutionContextplay.api.db.slick.DatabaseConfigProvider@play.db.NamedDatabase,上面的代码没有 运行。

我的问题是,如果没有 play application builder,我如何让这些东西绑定?

提前致谢。

答案取决于您是否真的想将 Play Framework 与您的 DAO 分离。

选项 1:不分离

您的主要方法可以在 val injector 行之前简单地添加以下行:

val application = new GuiceApplicationBuilder()
  .in(Environment(new File("."), this.getClass.getClassLoader, Mode.Prod))
  .build
Play.start(application)

选项 2:解耦

或者,您可以提供可注入的 类,它可以提供特定于环境的 ExecutionContext。如果你想注入 DatabaseConfigProvider,你将不得不执行进一步的抽象来移除对 Play 的直接依赖。注释将遵循特定于 Play 的抽象实现。


在我遇到这种情况的我自己的项目中,我选择了选项 1,因为对 Play 的依赖性对我的影响还不够严重。

GuiceInjectorBuilder 做这个把戏。

trait PlayInjector {
  lazy val injector = new GuiceInjectorBuilder().configure(Configuration.load(Environment.simple(mode = Mode.Dev)))
  .bindings(new BuiltinModule, new CoreModule, new SlickModule).disable(classOf[Application]).injector

  def closeInjector = injector.instanceOf[DefaultApplicationLifecycle].stop
}

BuiltinModule 绑定 Play 基本模块,例如 ExecutionContextExecutionContextExecutorActorSystem。 你可以让你自己的模块只绑定你需要的东西,使用 BuiltinModule 并禁用你不需要的 类 更简单。

如何使用它

object Foo extends PlayInjector {
  def main(args: Array[String]) = {
    val foo = injector.instanceOf[FooDAO]
    val bar = injector.instanceOf[BarDAO]
    //do something
    //when you finish things you want to do
    closeInjector
  }
}

因为像 PlaySlick 这样的一些模块使用 ApplicationLifecycle.addStopHook 来处理关闭操作。执行它们比调用 sys.exit()

更安全