[英]Play Scala Dependency injection: How to use it
I am trying to use Play 2.5 dependency injection. 我正在尝试使用Play 2.5依赖注入。 I have following class which makes a call to REST api and parses the response
我有以下类调用REST api并解析响应
class Client @Inject()(ws:WSClient, baseUrl: string) {
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {....}
....
}
The caller of the code looks like below 代码的调用者如下所示
var client = new Client(WS.client)
client.getResponse()
I am getting following warning. 我收到了警告。
object WS in package ws is deprecated: Inject WSClient into your component
不推荐使用包ws中的对象WS:将WSClient注入到组件中
I understand that i need to inject WS.Client instead of passing it explicitly to the Client constructor. 我知道我需要注入WS.Client而不是将它显式传递给Client构造函数。 But how do i do that?
但是我该怎么做?
=== Update === ===更新===
I don't want to inject Client or WSClient from the Controller. 我不想从Controller注入Client或WSClient。 My controller creates objects and classes at run time and i want those objects to create Client Object.
我的控制器在运行时创建对象和类,我希望这些对象创建客户端对象。 When i explicitly pass WS.client object to the Client object i get the above stated warning.
当我明确地将WS.client对象传递给Client对象时,我得到上述警告。
=== Update 2 === ===更新2 ===
I have a plugin architecture in my application. 我的应用程序中有一个插件架构。 When aa controller starts an action.
当控制器启动一个动作时。 It does not know what set of plugins it is going to execute.
它不知道它将执行哪些插件集。 Some plugins would not need a WSClient and some of them would.
有些插件不需要WSClient,其中一些插件也是如此。 So i dont want to couple the injection of WSClient into my controller.
所以我不想将WSClient的注入耦合到我的控制器中。 Each plugin independently decides if it wants to call a remote service.
每个插件独立决定是否要调用远程服务。 When a plugin decides to call the remote service, it should be able to inject WSClient in what ever client it wants to invoke.
当插件决定调用远程服务时,它应该能够在它想要调用的客户端中注入WSClient。
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1 (needs to call a remote api, create a client object, per say new Client(WS.Client)). 控制器操作 - >确定要执行的插件 - >执行插件--->插件1(需要调用远程api,创建一个客户端对象,根据新客户端(WS.Client))。 This is where the injection should happen, not at the controller.
这是注射应该发生的地方,而不是控制器。
Ok. 好。 I will assume you have two classes.
我假设你有两节课。 First we will have your
Client
class: 首先,我们将拥有您的
Client
类:
@Singleton // this is not necessary, I put it here so you know this is possible
class Client @Inject() (ws:WSClient, baseUrl: String) {
// Since this controller is not annotated with @Inject
// it WILL NOT be used when binding components
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {
// do something using ws object
}
}
Then you have another class that uses Client
, per instance, a controller: 然后你有另一个使用
Client
类,每个实例,一个控制器:
class MyController @Inject() (client: Client) extends Controller {
def someAction = Action {
// do something with client object
}
}
The main point here is that the controller did not need to create a Client
instance. 这里的要点是控制器不需要创建
Client
实例。 It was automatically injected by Guice. 它由Guice自动注入。
Moreover, your client class needs a baseUrl
and there is no place telling Play which value is needed there. 此外,您的客户端类需要一个
baseUrl
并且没有地方告诉Play那里需要哪个值。 If this is a configuration, than you can do something like this: 如果这是一个配置,那么你可以这样做:
import play.api.Configuration
class Client @Inject() (ws:WSClient, configuration: Configuration) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
But, if you really want your Client
object to receives a String
, then we need to tell Play which String needs to be injected : 但是,如果你真的希望你的
Client
对象接收一个String
,那么我们需要告诉Play需要注入哪个String :
package com.acme.modules
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class MyModule extends AbstractModule {
def configure() = {
bind(classOf[String])
.annotatedWith(Names.named("baseUrl")) // attention to the name here. It will be used below
.toInstance("http://api.example.com/")
}
}
And then enable this module by adding the following line to your application.conf
: 然后通过将以下行添加到
application.conf
来启用此模块:
play.modules.enabled += "com.acme.modules.MyModule"
After that, we will change Client
to be specific about which String
it is expecting: 之后,我们将更改
Client
以了解它所期望的String
:
import play.api.Configuration
// @Named needs to receive the same value defined at the module class.
class Client @Inject() (ws:WSClient, @Named("baseUrl") baseUrl: String) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
Give the structure you want/need: 给出你想要/需要的结构:
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1
Your code can also follow that path with classes like this: 您的代码也可以使用以下类的路径:
MyController -> PluginResolver -> Plugin
-> PluginRunner ->
And, then, you can have: 然后,你可以:
class MyController @Inject() (
pluginResolver: PluginResolver,
pluginRunner: PluginRunner
) extends Controller {
def action = Action {
val plugins = pluginsResolver.resolve(/* give a criteria to select plugins */)
val someResultFromPluginsExecution = pluginsRunner.run(plugins)
// map result from plugins execution to a play play.api.mvc.Result
// return the play.api.mvc.Result
}
}
import play.api.inject.Injector
class PluginResolver @Inject()(injector: Injector) {
def resolve(/* some criteria to resolve plugins */): Seq[Plugin] = {
val pluginsClasses = ... // find the necessary plugins based on the criteria
pluginsClasses.map { pluginClass => injector.instanceOf(pluginClass) }
}
}
// ExecutionContext is not really necessary, but maybe you want/need
// another thread pool to execute plugins
class PluginRunner @Inject()(implicit executionContext: ExecutionContext) {
def run(plugins: Seq[Plugin]): Seq[PluginExecutionResult] = {
// run the plugins
// return the result
}
}
trait Plugin {
def execute(): PluginExecutionResult
}
The real magic here happens at the PluginResolver
. 这里真正的魔力发生在
PluginResolver
。 It uses a play.api.inject.Injector
to create plugins instances and then your plugins can use Dependency Injection. 它使用
play.api.inject.Injector
来创建插件实例,然后你的插件可以使用依赖注入。 Per instance: 每个实例:
class PluginThatNeedsWSClient @Inject(wsClient: WSClient) extends Plugin {
def execute(): PluginExecutionResult = {
// Use wsClient to call a remote service
// return the execution result
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.