![](/img/trans.png)
[英]How to use dependency injection in Scala without Play Framework?
[英]Play Scala Dependency injection: How to use it
我正在嘗試使用Play 2.5依賴注入。 我有以下類調用REST api並解析響應
class Client @Inject()(ws:WSClient, baseUrl: string) {
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {....}
....
}
代碼的調用者如下所示
var client = new Client(WS.client)
client.getResponse()
我收到了警告。
不推薦使用包ws中的對象WS:將WSClient注入到組件中
我知道我需要注入WS.Client而不是將它顯式傳遞給Client構造函數。 但是我該怎么做?
===更新===
我不想從Controller注入Client或WSClient。 我的控制器在運行時創建對象和類,我希望這些對象創建客戶端對象。 當我明確地將WS.client對象傳遞給Client對象時,我得到上述警告。
===更新2 ===
我的應用程序中有一個插件架構。 當控制器啟動一個動作時。 它不知道它將執行哪些插件集。 有些插件不需要WSClient,其中一些插件也是如此。 所以我不想將WSClient的注入耦合到我的控制器中。 每個插件獨立決定是否要調用遠程服務。 當插件決定調用遠程服務時,它應該能夠在它想要調用的客戶端中注入WSClient。
控制器操作 - >確定要執行的插件 - >執行插件--->插件1(需要調用遠程api,創建一個客戶端對象,根據新客戶端(WS.Client))。 這是注射應該發生的地方,而不是控制器。
好。 我假設你有兩節課。 首先,我們將擁有您的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
}
}
然后你有另一個使用Client
類,每個實例,一個控制器:
class MyController @Inject() (client: Client) extends Controller {
def someAction = Action {
// do something with client object
}
}
這里的要點是控制器不需要創建Client
實例。 它由Guice自動注入。
此外,您的客戶端類需要一個baseUrl
並且沒有地方告訴Play那里需要哪個值。 如果這是一個配置,那么你可以這樣做:
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
}
}
但是,如果你真的希望你的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/")
}
}
然后通過將以下行添加到application.conf
來啟用此模塊:
play.modules.enabled += "com.acme.modules.MyModule"
之后,我們將更改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
}
}
給出你想要/需要的結構:
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1
您的代碼也可以使用以下類的路徑:
MyController -> PluginResolver -> Plugin
-> PluginRunner ->
然后,你可以:
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
}
這里真正的魔力發生在PluginResolver
。 它使用play.api.inject.Injector
來創建插件實例,然后你的插件可以使用依賴注入。 每個實例:
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.