如何正确使用 DI 注入 Play 控制器的构造函数?
How do I correctly use DI to inject a Play controller's constructor?
我正在将 Play 2.3.x 应用程序迁移到 Play 2.5.x,但在使用依赖项注入时遇到了一些问题。
在 2.3 中,我有一个特性 HasRemoteActor
,控制器会混入该特性以根据配置引用某个远程 actor。由于这需要应用程序的配置对象,因此现在需要将其变为 class 以便可以注入配置。这是我的尝试:
/*
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
并在我的注入路由的关联控制器前加上 @
,但仍然发现相同的运行时异常。
有人有什么建议吗?
我不会为此使用继承。相反,我会选择这样的东西(我假设你使用的是 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
}
}
所以诀窍在于,通过使用提供程序,您可以将绑定等的初始化推迟到需要的时候。
我正在将 Play 2.3.x 应用程序迁移到 Play 2.5.x,但在使用依赖项注入时遇到了一些问题。
在 2.3 中,我有一个特性 HasRemoteActor
,控制器会混入该特性以根据配置引用某个远程 actor。由于这需要应用程序的配置对象,因此现在需要将其变为 class 以便可以注入配置。这是我的尝试:
/*
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
并在我的注入路由的关联控制器前加上 @
,但仍然发现相同的运行时异常。
有人有什么建议吗?
我不会为此使用继承。相反,我会选择这样的东西(我假设你使用的是 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
}
}
所以诀窍在于,通过使用提供程序,您可以将绑定等的初始化推迟到需要的时候。