Estou tentando usar a injeção de dependência do Play 2.5. Eu tenho a seguinte classe que faz uma chamada para API REST e analisa a resposta
class Client @Inject()(ws:WSClient, baseUrl: string) {
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {....}
....
}
O chamador do código é semelhante a abaixo
var client = new Client(WS.client)
client.getResponse()
Estou recebendo o seguinte aviso.
O objeto WS no pacote ws foi descontinuado: injete o WSClient no seu componente
Entendo que preciso injetar o WS.Client em vez de passá-lo explicitamente ao construtor Client. Mas como eu faço isso?
=== Atualização ===
Não quero injetar Client ou WSClient deo controlador. Meu controlador cria objetos e classes em tempo de execução e eu quero que esses objetos criem Objeto Cliente. Quando passo explicitamente o objeto WS.client para o objeto Client, recebo o aviso acima.
=== Atualização 2 ===
Eu tenho uma arquitetura de plugins no meu aplicativo. Quando um controlador inicia uma ação. Ele não sabe qual conjunto de plugins será executado. Alguns plug-ins não precisariam de um WSClient e outros. Então, eu não quero acoplar a injeção do WSClient no meu controlador. Cada plug-in decide independentemente se deseja chamar um serviço remoto. Quando um plug-in decide chamar o serviço remoto, ele deve ser capaz de injetar o WSClient em qualquer cliente que ele queira chamar.
Ação do controlador -> Determinar plugins paraExecute -> Execute Plugins ---> Plugin1 (precisa chamar uma API remota, criar um objeto de cliente, por exemplo, novo cliente (WS.Client)). É aqui que a injeção deve ocorrer, não no controlador.
Respostas:
8 para resposta № 1Está bem. Eu vou assumir que você tem duas classes. Primeiro teremos o seu Client
classe:
@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
}
}
Então você tem outra classe que usa Client
, por exemplo, um controlador:
class MyController @Inject() (client: Client) extends Controller {
def someAction = Action {
// do something with client object
}
}
O ponto principal aqui é que o controlador não precisou criar um Client
instância. Foi injetado automaticamente pelo Guice.
Além disso, sua classe de cliente precisa de um baseUrl
e não há lugar para dizer ao Play qual valor é necessário lá. Se essa é uma configuração, você pode fazer algo assim:
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
}
}
Mas se você realmente quer seu Client
objeto para recebe um String
, então precisamos diga ao Play qual String precisa ser injetada:
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/")
}
}
E ative este módulo adicionando a seguinte linha ao seu application.conf
:
play.modules.enabled += "com.acme.modules.MyModule"
Depois disso, vamos mudar Client
ser específico sobre quais String
está esperando:
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
}
}
Atualizar após a edição da pergunta:
Dê a estrutura que você deseja / precisa:
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1
Seu código também pode seguir esse caminho com classes como esta:
MyController -> PluginResolver -> Plugin
-> PluginRunner ->
E, então, você pode ter:
Controlador:
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
}
}
Classes de plug-in:
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
}
A verdadeira magia aqui acontece no PluginResolver
. Ele usa um play.api.inject.Injector
para criar instâncias de plug-ins e seus plug-ins podem usar a Injeção de Dependência. Por exemplo:
class PluginThatNeedsWSClient @Inject(wsClient: WSClient) extends Plugin {
def execute(): PluginExecutionResult = {
// Use wsClient to call a remote service
// return the execution result
}
}
Referência:
3 para resposta № 2
Eu vi esse post incrível na semana passada: http://www.schibsted.pl/2016/04/dependency-injection-play-framework-scala/