簡體   English   中英

播放Scala依賴注入:如何使用它

[英]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
    }
}

參考:

  1. Scala:依賴注入
  2. Scala:玩WS API
  3. play.api.inject.Injector

上周我看到了這篇很棒的帖子: http//www.schibsted.pl/2016/04/dependency-injection-play-framework-scala/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM