创建一个类实例,通过Playframework Guice在Scala中独立完成DI

时间:2016-07-20 02:53:28

标签: scala playframework guice

我正在开发Playframework2.5,其中包括游戏和与之相关的程序,如批处理。

当前的项目结构就像

/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之类的内容,因此以上代码无法运行。

我的问题是,如果没有播放应用程序构建器,我怎么能让这些东西受到约束?

提前致谢。

2 个答案:

答案 0 :(得分:1)

答案取决于您是否想要将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的依赖程度对我来说影响不够严重。

答案 1 :(得分:0)

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()