【问题标题】:How do I correctly use DI to inject a Play controller's constructor?如何正确使用 DI 注入 Play 控制器的构造函数?
【发布时间】:2017-02-14 13:20:25
【问题描述】:

我正在将 Play 2.3.x 应用程序迁移到 Play 2.5.x,但在使用依赖注入时遇到了一些问题。

在 2.3 中,我有一个特征 HasRemoteActor,控制器将混合它以根据配置引用一些远程参与者。由于这需要应用程序的配置对象,因此现在需要将其变成一个类,以便可以注入配置。这是我的尝试:

/*
   Dummy controller that has environment and configuration manually injected.
*/
class ConfigurationController(env: play.api.Environment,
                              conf: play.api.Configuration) extends Controller {

}

/*
  Dummy controller that has environment and configuration manually injected, but 
  sets up a remote client.
*/ 
class RemoteActorController(env: play.api.Environment, conf: play.api.Configuration)
  extends ConfigurationController(env, conf) {

  protected val remoteActorName = "foo"
  private val remoteActorConf = conf.underlying.getConfig(remoteActorName)
  private val system = ActorSystem("HttpServerSystem", ConfigFactory.load())

  private val tcpInfo = remoteActorConf.getConfig("akka.remote.netty.tcp")
  private val hostname = tcpInfo.getString("hostname")
  private val port = tcpInfo.getString("port")

  val path = s"akka.tcp://PubSubMember@$hostname:$port/system/receptionist"

  private val initialContacts = Set(ActorPath.fromString(path))


  protected val client = system.actorOf(
    ClusterClient.props(ClusterClientSettings(system).withInitialContacts(
        initialContacts)),
    "ClusterClient"
  )
}

/*
   Actual controller whose actions correspond to endpoints in `conf/routes`.
*/
@Singleton
class BarController @Inject()(env: play.api.Environment,
                              conf: play.api.Configuration) extends
    RemoteActorController(env, conf) {

    // ...

}

但是,当我启动我的应用程序时,我发现无论端口号如何,actor 系统总是无法找到它的端口(即使没有在该端口上监听)。

play.api.UnexpectedException: Unexpected exception[ProvisionException: Unable to provision, see the following errors:

1) Error injecting constructor, org.jboss.netty.channel.ChannelException: Failed to bind to: /127.0.0.1:8888

注入的时间似乎有问题,但我对 DI 太陌生,无法调试它。

我尝试将routesGenerator := InjectedRoutesGenerator 添加到我的build.sbt 并在我注入的路由的关联控制器前面加上@,但仍然发现相同的运行时异常。

有人有建议吗?

【问题讨论】:

  • “绑定失败”通常意味着您已经有一个应用程序使用此端口,请尝试更改您的配置以使用其他端口。
  • @vdebergue 端口没有被 TCP 或 UDP 使用。

标签: scala dependency-injection playframework guice akka-remoting


【解决方案1】:

我不会为此使用继承。相反,我会选择这样的东西(我假设你正在使用 guice):

@Singleton
class RemoteActorAdapter @Inject() (env: Environment, conf: Configuration) {

  // all other initialization code
  val client: ActorRef = ???

}

在想要使用这些东西的控制器中:

class MyController @Inject() (remoteAdapterProvider: Provider[RemoteActorAdapter]) extends Controller {
  def index = Action {
    remoteAdapterProvider.get.client ! Hello
  }
}

所以诀窍在于,通过使用提供程序,您可以将绑定等的初始化推迟到需要的时候。

【讨论】:

  • 虽然我比我尝试过的方法更喜欢这种方法,但我仍然发现我遇到了绑定错误。
  • 我认为它没有将RemoteActorAdapter 绑定为单例...你知道如何在Module.scala 中正确绑定它吗?
  • 所以使用Provider 的目的是推迟初始化。初始化是否第一次工作,但随后失败,因为它没有重新使用实例?还是第一次尝试也失败了?在前一种情况下,您可以尝试这样的操作:bind(classOf[RemoteActorAdapter]).to(classOf[RemoteActorAdapter]).in(new SingletonScope())。在后一种情况下,我怀疑这与依赖注入有关。
  • 我想我发现了问题……它与 DI 完全脱节。相反,它是 ConfigFactory.load() 调用 - 这导致了问题并显示为绑定错误!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多