加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 在Play Framework 2.5中使用抽象类和对象进行依赖注入

发布时间:2020-12-16 09:29:16 所属栏目:安全 来源:网络整理
导读:我正在尝试从Play 2.4迁移到2.5,避免弃用. 我有一个抽象类Microservice,我从中创建了一些对象. Microservice类的一些函数使用play.api.libs.ws.WS来发出HTTP请求,并使用play.Play.application.configuration来读取配置. 以前,我需要的只是一些进口,如: impo
我正在尝试从Play 2.4迁移到2.5,避免弃用.

我有一个抽象类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

但现在你should use dependency injection to use WS和to use access the current Play application.

我有类似的东西(缩短):

abstract class Microservice(serviceName: String) {
    // ...
    protected lazy val serviceURL: String = play.Play.application.configuration.getString(s"microservice.$serviceName.url")
    // ...and functions using WS.url()...
}

对象看起来像这样(缩短):

object HelloWorldService extends Microservice("helloWorld") {
    // ...
}

不幸的是,我不明白如何将所有的东西(WS,配置,ExecutionContect)放入抽象类中以使其工作.

我试着把它改成:

abstract class Microservice @Inject() (serviceName: String,ws: WSClient,configuration: play.api.Configuration)(implicit context: scala.concurrent.ExecutionContext) {
    // ...
}

但这并没有解决问题,因为现在我也必须改变对象,我无法弄清楚如何.

我试图将对象变成@Singleton类,如:

@Singleton
class HelloWorldService @Inject() (implicit ec: scala.concurrent.ExecutionContext) extends Microservice ("helloWorld",configuration: play.api.Configuration) { /* ... */ }

我尝试了各种各样的组合,但我没有到达任何地方,我觉得我在这里并没有真正走上正轨.

任何想法如何在不使事情如此复杂的情况下以正确的方式使用WS之类的东西(不使用弃用的方法)?

解决方法

这与Guice如何处理继承更为相关,如果你没有使用Guice,你必须做的就是你要做的事情,Guice是在超类中声明参数并在子类中调用超级构造函数. Guice甚至建议在 its docs:

Wherever possible,use constructor injection to create immutable objects. Immutable objects are simple,shareable,and can be composed.

Constructor injection has some limitations:

  • Subclasses must call super() with all dependencies. This makes constructor injection cumbersome,especially as the injected base class changes.

在纯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;
  }
}

依赖注入不会改变它,你只需要添加注释来指示如何注入依赖项.在这种情况下,由于Base类是抽象的,然后不能创建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;
  }
}

翻译成Scala,我们会有这样的事情:

abstract class Base(dep: Dependency) {
  // something else
}

class Child @Inject() (anotherDep: AnotherDependency,dep: Dependency) extends Base(dep) {
  // something else
}

现在,我们可以重写您的代码以使用这些知识并避免弃用的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) {
    // ...
}

最后一点是隐式ExecutionContext,这里有两个选项:

>使用default execution context,这将是play.api.libs.concurrent.Execution.Implicits.defaultContext
>使用other thread pools

这取决于您,但您可以轻松注入ActorSystem来查找调度程序.如果您决定使用自定义线程池,则可以执行以下操作:

abstract class Microservice(serviceName: String,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,actorSystem: ActorSystem)
    extends Microservice("helloWorld",ws,actorSystem) {
    // ...
}

如何使用HelloWorldService?

现在,您需要了解两件事,以便在需要的地方正确注入HelloWorldService实例.

HelloWorldService从哪里获取其依赖项?

Guice docs有一个很好的解释:

Dependency Injection

Like the factory,dependency injection is just a design pattern. The core principle is to separate behaviour from dependency resolution.

The dependency injection pattern leads to code that’s modular and testable,and Guice makes it easy to write. To use Guice,we first need to tell it how to map our interfaces to their implementations. This configuration is done in a Guice module,which is any Java class that implements the Module interface.

然后,Playframework为WSClient和Configuration声明了模块.两个模块都为Guice提供了有关如何构建这些依赖关系的足够信息,并且有一些模块可以描述如何构建WSClient和Configuration所需的依赖关系.同样,Guice docs有一个很好的解释:

With dependency injection,objects accept dependencies in their constructors. To construct an object,you first build its dependencies. But to build each dependency,you need its dependencies,and so on. So when you build an object,you really need to build an object graph.

在我们的例子中,对于HelloWorldService,我们使用constructor injection来启用Guice来设置/创建我们的对象图.

如何注入HelloWorldService?

就像WSClient有一个模块来描述一个实现如何绑定到接口/ trait,我们也可以为HelloWorldService做同样的事情. Play docs有关于如何创建和配置模块的清晰说明,所以我在此不再重复.

但是在创建模块之后,要向控制器注入HelloWorldService,您只需将其声明为依赖项:

class MyController @Inject() (service: Microservice) extends Controller {

    def index = Action {
        // access "service" here and do whatever you want 
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读