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

假装没事ソ 提交于 2020-01-05 04:23:21

问题


I'm working on Playframework2.5 with play-slick and programs related to it such as batch.

current project structure is like

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

I'm using Guice DI almost everywhere include Database Access Object(DAO). And interfaces in core are bound in Module placed in core which end up getting inherited by Module in root project.

Core Module(/rootPlayProject/core/CoreModule.scala)

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

Root Module(/rootPlayProject/Modules.scala)

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

This works pretty well as Playframework application though, I would like to use core module for batch programs and I would like to run the batches without playframework.

so far I tried something like this

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

but since my DAO's requiring things Playframework create such as ExecutionContext,play.api.db.slick.DatabaseConfigProvider and @play.db.NamedDatabase, above code does not run.

My question is, How can I let those things get bound without play application builder?

Thanks in advance.


回答1:


The answer depends on whether or not you want to actually decouple Play Framework from your DAOs.

Option 1: Don't Decouple

Your main method could simply have the following lines preceeding your val injector line:

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

Option 2: Decouple

Or, you can provide injectable classes that can provide the ExecutionContext specific to the environment. If you want to inject the DatabaseConfigProvider, you will have to perform further abstractions to remove the direct dependency on Play. The annotation will follow the Play-specific implementation of the abstraction.


In the case of my own project where I encountered this scenario, I opted for Option 1, as the dependency on Play wasn't severe enough of an impact for me.




回答2:


GuiceInjectorBuilder do the trick.

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 binds Play basic modules such as ExecutionContext, ExecutionContextExecutor or ActorSystem. You can make your own Module to bind only things you need to though, using BuiltinModule and disable classes you don't need is simpler.

how to use it

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
  }
}

Since some modules like PlaySlick uses ApplicationLifecycle.addStopHook to handle closing operation. It's safer to execute them instead of just call sys.exit()



来源:https://stackoverflow.com/questions/38471551/create-an-instance-of-class-which-does-di-via-playframework-guice-independently

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!