在Play Framework 2.5中使用抽象类和对象进行依赖注入 [英] Dependency injection with abstract class and object in Play Framework 2.5

查看:69
本文介绍了在Play Framework 2.5中使用抽象类和对象进行依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从Play 2.4迁移到2.5,以避免出现过时的情况.

I'm trying to migrate from Play 2.4 to 2.5 avoiding deprecated stuff.

我有一个abstract class Microservice,我从中创建了一些对象. Microservice类的某些功能使用play.api.libs.ws.WS发出HTTP请求,还使用play.Play.application.configuration读取配置.

I had an abstract class Microservice from which I created some objects. Some functions of the Microservice class used play.api.libs.ws.WS to make HTTP requests and also play.Play.application.configuration to read the configuration.

以前,我所需要做的只是一些导入,例如:

Previously, all I needed was some imports like:

import play.api.libs.ws._
import play.api.Play.current
import play.api.libs.concurrent.Execution.Implicits.defaultContext

但是现在您应该使用依赖项注入来使用WS ,并且还应该使用我有这样的东西(缩短):

I have something like this (shortened):

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

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

An object looks something like this (shortened):

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

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

Unfortunately I don't understand how I get all the stuff (WS, configuration, ExecutionContect) into the abstract class to make it work.

我试图将其更改为:

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

但这不能解决问题,因为现在我也必须更改对象,而且我不知道怎么做.

But this doesn't solve the problem, because now I have to change the object too, and I can't figure out how.

我试图将object变成@Singleton class,例如:

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

我尝试了各种各样的组合,但是我什么也没走,我觉得我在这里并不是很正确.

I tried all sorts of combinations, but I'm not getting anywhere and I feel I'm not really on the right track here.

有什么主意如何以正确的方式使用WS之类的东西(不使用不推荐使用的方法)而又不会使事情变得如此复杂?

Any ideas how I can use things like WS the proper way (not using deprecated methods) without making things so complicated?

推荐答案

这与Guice处理继承的方式更为相关,如果您不使用Guice,则必须完全按照您要执行的操作进行操作,这就是在向Guice声明参数.父类,并在您的子类上调用超级构造函数. Guice甚至在其文档中建议它:

This is more related to how Guice handles inheritance and you have to do exactly what you would do if you were not using Guice, which is declaring the parameters to the superclass and calling the super constructor at your child classes. Guice even suggest it at 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:

  • 子类必须调用具有所有依赖项的super().这使构造函数注入变得麻烦,尤其是随着注入的基类的更改.
  • Subclasses must call super() with all dependencies. This makes constructor injection cumbersome, especially as the injected base class changes.

在纯Java中,这意味着要执行以下操作:

In pure Java, it will means doing something like this:

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类是abstract,因此无法创建Base的实例,我们可以跳过它而只注释Child类:

Dependency injection won't change that and you will just have to add the annotations to indicate how to inject the dependencies. In this case, since Base class is abstract, and then no instances of Base can be created, we may skip it and just annotate Child class:

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后,我们将得到以下内容:

Translating to Scala, we will have something like this:

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

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

现在,我们可以重写您的代码以使用此知识,并避免使用不推荐使用的API:

Now, we can rewrite your code to use this knowledge and avoid deprecated APIs:

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) {
    // ...
}

最后一点是implicit ExecutionContext,在这里我们有两个选择:

The last point is the implicit ExecutionContext and here we have two options:

  1. 使用默认执行上下文play.api.libs.concurrent.Execution.Implicits.defaultContext
  2. 使用其他线程池
  1. Use the default execution context, which will be play.api.libs.concurrent.Execution.Implicits.defaultContext
  2. Use other thread pools

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

This depends on you, but you can easily inject an ActorSystem to lookup the dispatcher. If you decide to go with a custom thread pool, you can do something like this:

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) {
    // ...
}

如何使用HelloWorldService?

现在,您需要了解两件事,以便在需要的位置正确插入HelloWorldService实例.

How to use HelloWorldService?

Now, there are two things you need to understand in order to proper inject an instance of HelloWorldService where you need it.

Guice文档对此有很好的解释:

依赖注入

像工厂一样,依赖注入只是一种设计模式.核心原则是将行为与依赖解决方案分开.

Dependency Injection

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

依赖注入模式导致代码模块化和可测试,而Guice使其易于编写.要使用Guice,我们首先需要告诉它如何将接口映射到其实现.此配置是在Guice模块中完成的,该模块是实现Module接口的任何Java类.

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 Guice文档对此有很好的解释:

And then, Playframework declare modules for WSClient and for Configuration. Both modules gives Guice enough information about how to build these dependencies, and there are modules to describe how to build the dependencies necessary for WSClient and Configuration. Again, Guice docs has a good explanation about it:

通过依赖注入,对象在其构造函数中接受依赖.要构造一个对象,首先要建立它的依赖关系.但是要构建每个依赖项,您需要其依赖项,依此类推.因此,在构建对象时,您确实需要构建对象图.

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,我们正在使用构造函数注入来启用Guice设置/创建对象图.

In our case, for HelloWorldService, we are using constructor injection to enable Guice to set/create our object graph.

就像WSClient一样,有一个模块描述实现如何绑定到接口/特征,我们可以对HelloWorldService做同样的事情. 播放文档对于如何创建和配置模块有明确的解释,所以我不会在这里重复一下.

Just like WSClient has a module to describe how an implementation is binded to an interface/trait, we can do the same for HelloWorldService. Play docs has a clear explanation about how to create and configure modules, so I won't repeat it here.

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

But after creating an module, to inject a HelloWorldService to your controller, you just declare it as a dependency:

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

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

这篇关于在Play Framework 2.5中使用抽象类和对象进行依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆