【问题标题】:Play Framework 2.5.x: Inject Environment in a ModulePlay Framework 2.5.x:在模块中注入环境
【发布时间】:2016-06-07 11:41:49
【问题描述】:

Play 应用包含一个在应用启动期间初始化 DB/Tables 的自定义模块。 从 v2.5.x 开始,需要注入应用程序模式(prod、test、dev)而不是使用 Current。我尝试在模块代码中应用以下注入参数,但看到编译错误。

trait MyInit[Environment] {
  val env: Environment
}

class MyInitClass[Environment] @Inject() (
  val env: Environment
) extends MyInit[Environment] {
  initializeDB()

  def initializeDB() = {
    Logger.info("MyINIT: Creating database...")
    MyDBService.createDatabase()
    Logger.info("MyINIT: Creating tables...")
    MyDBService.createTables()
  }
}

class MyModule(environment: Environment,
             configuration: Configuration) extends AbstractModule with ScalaModule {
  def configure() = {
    bind[MyInit[Environment]].to[MyInitClass[Environment]].asEagerSingleton()
  }
}

在 Play 2.5.x (Scala) 中的模块初始化期间是否有规定的查找环境/模式的方法?

【问题讨论】:

  • 编译错误说明了什么?我也不清楚为什么你在特征和扩展特征的类中都使用类型参数Environment。它应该在没有类型参数的情况下工作。
  • “实现限制:目前不支持注释上的多个参数列表。”我正在尝试访问/传递环境对象以查找模式,以便我可以初始化所需的数据库。
  • 这很有趣。你如何编译你的代码?我昨天或前一天在 Stackoverflow 上看到了一个类似编译错误的问题,但由于某种原因它很快就被删除了。我想问题出在 Intellij IDEA 上。
  • 无论如何我认为将依赖项传递给AbstractModule 构造函数是不合格的。您需要将它们注入 MyInitClass 的构造函数或字段。
  • 另外,据我所知,您不能仅使用带有类型参数的bind 来绑定泛型类型。你需要使用TypeLiteralthejavablog.wordpress.com/2008/11/17/…

标签: scala dependency-injection guice playframework-2.5


【解决方案1】:

我将重写您的代码,以消除这里可能出现的小混乱。这正是您对类型参数进行了小幅重命名的代码:

import javax.inject.Inject
import play.api.Logger
import play.api.Environment
import play.api.Configuration
import com.google.inject.AbstractModule

trait MyInit[T] {
  val env: T
}

class MyInitClass[T] @Inject() (val env: T) extends MyInit[T] {
  initializeDB()

  def initializeDB() = {
    Logger.info("MyINIT: Creating database...")
    MyDBService.createDatabase()
    Logger.info("MyINIT: Creating tables...")
    MyDBService.createTables()
  }
}

我重命名了类型参数以避免与play.api.Environment 混淆。我觉得这不是您想要的,因为MyInitClass 不会在任何地方主动收到play.api.Environment

如果你想做一些初始化,为什么不使用更简单直接的方法:

import com.google.inject.AbstractModule
import play.api.{Configuration, Environment, Mode}

class DatabaseInitializerModule(
  environment: Environment,
  configuration: Configuration
) extends AbstractModule {

  def configure() = {
    environment.mode match {
      case Mode.Dev =>
        // Start dev database
      case Mode.Test =>
        // Start test database
      case Mode.Prod =>
        // Start prod database
    }
  }
}

编辑:

替换GlobalSettings.onStart 的更惯用和推荐的方法是described at the docs

GlobalSettings.beforeStartGlobalSettings.onStart:启动时需要发生的任何事情现在都应该在依赖注入类的构造函数中发生。一个类将在依赖注入框架加载它时执行它的初始化。如果您需要预先初始化(因为您需要在应用程序实际启动之前执行一些代码),define an eager binding

所以,我们可以将你的代码改写成这样:

import javax.inject.Inject

import play.api.{Environment, Mode}
import com.google.inject.AbstractModule

class DatabaseInitializer @Inject()(environment: Environment) {
  environment.mode match {
    case Mode.Dev =>
    // Start dev database
    case Mode.Test =>
    // Start test database
    case Mode.Prod =>
    // Start prod database
  }
}

class DatabaseInitializerModule extends AbstractModule {
  override def configure() = {
    bind(classOf[DatabaseInitializer]).asEagerSingleton()
  }
}

请注意我是如何删除 trait 的,因为它在这里没有添加任何价值。

【讨论】:

  • 参数重命名在 IntelliJ 中仍然显示为错误。但是下面的过程更直接,并且可以正常工作。但是我想知道 Play 2.5.x 中是否有规定的设计模式,用于基于使用 DI 的模式进行初始化。如果不需要这样的解决方案,我将把它标记为答案。谢谢!
  • 我重命名了类型参数,以避免混淆其名称和play.api.Environment。我的印象是你在这里混淆了这些概念。无论如何,这里描述了最推荐的替换/删除 GlobalSettings 的方法:playframework.com/documentation/2.5.x/GlobalSettings#Scala。我将编辑我的答案,以使您的代码适应对我来说更惯用的东西。
  • 是的,特征使用是多余的。就我而言,我添加了一行直接在configure() 内部的db 连接器对象中设置模式。这样,MyInitClass 保持完全相同,并且对初始化 db 的调用将在已设置模式的实际 db init 对象上进行。但是,我将您的答案标记为已接受。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 2016-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多