Nic*_*ick 17 scala playframework playframework-2.5
我正在尝试从Play 2.4迁移到2.5,避免弃用.
我有一个abstract class Microservice从中创建了一些对象.Microservice该类的某些函数用于play.api.libs.ws.WS发出HTTP请求以及play.Play.application.configuration读取配置.
以前,我需要的只是一些进口,如:
import play.api.libs.ws._
import play.api.Play.current
import play.api.libs.concurrent.Execution.Implicits.defaultContext
Run Code Online (Sandbox Code Playgroud)
但是现在你应该使用依赖注入来使用WS,也可以使用访问当前的Play应用程序.
我有类似的东西(缩短):
abstract class Microservice(serviceName: String) {
// ...
protected lazy val serviceURL: String = play.Play.application.configuration.getString(s"microservice.$serviceName.url")
// ...and functions using WS.url()...
}
Run Code Online (Sandbox Code Playgroud)
对象看起来像这样(缩短):
object HelloWorldService extends Microservice("helloWorld") {
// ...
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,我不明白如何将所有的东西(WS,配置,ExecutionContect)放到抽象类中以使其工作.
我试着把它改成:
abstract class Microservice @Inject() (serviceName: String, ws: WSClient, configuration: play.api.Configuration)(implicit context: scala.concurrent.ExecutionContext) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
但这并没有解决问题,因为现在我也要改变对象,我无法弄清楚如何.
我试图把它object变成一个@Singleton class,像:
@Singleton
class HelloWorldService @Inject() (implicit ec: scala.concurrent.ExecutionContext) extends Microservice ("helloWorld", ws: WSClient, configuration: play.api.Configuration) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
我尝试了各种各样的组合,但我没有到达任何地方,我觉得我在这里并没有真正走上正轨.
任何想法如何在不使事情如此复杂的情况下以正确的方式(不使用弃用的方法)使用WS之类的东西?
mar*_*ira 16
这与Guice如何处理继承更为相关,如果你没有使用Guice,你必须做的就是你要做的事情,Guice是在超类中声明参数并在子类中调用超级构造函数.Guice甚至在其文档中提出建议:
尽可能使用构造函数注入来创建不可变对象.不可变对象简单,可共享,并且可以组合.
构造函数注入有一些限制:
- 子类必须使用所有依赖项调用super().这使构造函数注入变得麻烦,尤其是当注入的基类发生变化时.
在纯Java中,它意味着做这样的事情:
public abstract class Base {
private final Dependency dep;
public Base(Dependency dep) {
this.dep = dep;
}
}
public class Child extends Base {
private final AnotherDependency anotherDep;
public Child(Dependency dep, AnotherDependency anotherDep) {
super(dep); // guaranteeing that fields at superclass will be properly configured
this.anotherDep = anotherDep;
}
}
Run Code Online (Sandbox Code Playgroud)
依赖注入不会改变它,您只需添加注释以指示如何注入依赖项.在这种情况下,由于Base类是abstract,然后没有Base可以创建的实例,我们可以跳过它,只是注释Child类:
public abstract class Base {
private final Dependency dep;
public Base(Dependency dep) {
this.dep = dep;
}
}
public class Child extends Base {
private final AnotherDependency anotherDep;
@Inject
public Child(Dependency dep, AnotherDependency anotherDep) {
super(dep); // guaranteeing that fields at superclass will be properly configured
this.anotherDep = anotherDep;
}
}
Run Code Online (Sandbox Code Playgroud)
翻译成Scala,我们会有这样的事情:
abstract class Base(dep: Dependency) {
// something else
}
class Child @Inject() (anotherDep: AnotherDependency, dep: Dependency) extends Base(dep) {
// something else
}
Run Code Online (Sandbox Code Playgroud)
现在,我们可以重写您的代码以使用这些知识并避免弃用的API:
abstract class Microservice(serviceName: String, configuration: Configuration, ws: WSClient) {
protected lazy val serviceURL: String = configuration.getString(s"microservice.$serviceName.url")
// ...and functions using the injected WSClient...
}
// a class instead of an object
// annotated as a Singleton
@Singleton
class HelloWorldService(configuration: Configuration, ws: WSClient)
extends Microservice("helloWorld", configuration, ws) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
最后一点是implicit ExecutionContext,在这里我们有两个选择:
这取决于您,但您可以轻松注入ActorSystem查找调度程序.如果您决定使用自定义线程池,则可以执行以下操作:
abstract class Microservice(serviceName: String, configuration: Configuration, ws: WSClient, actorSystem: ActorSystem) {
// this will be available here and at the subclass too
implicit val executionContext = actorSystem.dispatchers.lookup("my-context")
protected lazy val serviceURL: String = configuration.getString(s"microservice.$serviceName.url")
// ...and functions using the injected WSClient...
}
// a class instead of an object
// annotated as a Singleton
@Singleton
class HelloWorldService(configuration: Configuration, ws: WSClient, actorSystem: ActorSystem)
extends Microservice("helloWorld", configuration, ws, actorSystem) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
HelloWorldService?现在,您需要了解两件事,以便正确地注入HelloWorldService您需要的实例.
HelloWorldService得到它的依赖?Guice文档对此有一个很好的解释:
依赖注入像工厂一样,依赖注入只是一种设计模式.核心原则是将行为与依赖性解析分开.
依赖注入模式导致代码模块化和可测试,而Guice使编写变得容易.要使用Guice,我们首先需要告诉它如何将接口映射到它们的实现.此配置在Guice模块中完成,该模块是实现Module接口的任何Java类.
然后,Playframework为WSClient和Configuration声明模块.这两个模块都提供了关于如何建立这些依赖吉斯足够的信息,并且有模块描述如何建立必要的依赖WSClient和Configuration.同样,Guice文档对此有一个很好的解释:
通过依赖注入,对象接受其构造函数中的依赖项.要构造对象,首先要构建其依赖项.但是要构建每个依赖项,您需要它的依赖项,依此类推.因此,在构建对象时,您确实需要构建对象图.
在我们的例子中HelloWorldService,我们正在使用构造函数注入来使Guice能够设置/创建我们的对象图.
HelloWorldService注射?就像WSClient有一个模块来描述一个实现如何绑定到接口/特性,我们可以做同样的事情HelloWorldService.播放文档有关于如何创建和配置模块的明确说明,所以我在此不再重复.
但是在创建模块之后,要向HelloWorldService控制器注入一个模块,您只需将其声明为依赖项:
class MyController @Inject() (service: Microservice) extends Controller {
def index = Action {
// access "service" here and do whatever you want
}
}
Run Code Online (Sandbox Code Playgroud)