文档

§将插件迁移到模块

注意: 从 2.5.x 版本开始,已弃用的 play.Plugin 系统已移除。

如果您已实现 Play 插件,请考虑将您的实现迁移到使用 play.api.inject.Module,而不是已弃用的 Java play.Plugin 或 Scala play.api.Plugin 类型。

旧的 Plugin API 与使用 Module 的新 API 之间的最大区别在于,使用后者,我们将完全拥抱依赖注入 (DI) - 您可以在 此处 阅读 Play 为什么对 DI 持有意见。

§基本原理

使用旧的 Plugin API 时,您需要提供一个 play.plugins 文件,其中包含要加载的插件的编号和完全限定名称。Play 使用该编号来确定插件之间的总顺序。这种方法很脆弱,因为很难确保该编号对应于初始化顺序中的正确位置,因此插件的依赖项在插件初始化之前就被初始化了。此外,没有办法避免两个插件使用相同的编号。这两个问题都通过使用 DI 得到了解决。组件现在显式地将它们的依赖项声明为构造函数参数,并在构造函数中进行初始化。

§创建 Module

首先创建一个从 play.api.inject.Module 继承的类,并为 bindings 方法提供实现。在此方法中,您应该将类型连接到具体实现,以便您的模块提供的组件可以在用户代码或其他模块中注入。下面是一个示例。

在 Java 中

import play.api.Configuration;
import play.api.Environment;
import play.api.inject.Binding;
import play.api.inject.Module;

import scala.collection.Seq;

public class MyModule extends Module {
  public Seq<Binding<?>> bindings(Environment environment, Configuration configuration) {
    return seq(
      bind(MyComponent.class).to(MyComponentImpl.class)
    );
  }
}

在 Scala 中

import play.api.Configuration
import play.api.Environment

class MyModule extends Module {
  def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = {
    Seq(
      bind[MyComponent].to[MyComponentImpl]
    )
  }
}

请注意,如果您定义的组件需要另一个组件,您只需将所需的组件作为构造函数的依赖项添加,并在构造函数前面加上 @javax.inject.Inject 注解。然后,DI 框架将负责其余工作。

注意: 如果组件 B 需要 A,则 B 只有在 A 初始化后才会被初始化。

下面是一个名为 MyComponentImpl 的组件需要 ApplicationLifecycle 组件的示例。

在 Java 中

import javax.inject.Inject;
import play.inject.ApplicationLifecycle;
import play.libs.F;

public interface MyComponent {}

class MyComponentImpl implements MyComponent {
  @Inject
  public MyComponentImpl(ApplicationLifecycle lifecycle) {
    // previous contents of Plugin.onStart
    lifecycle.addStopHook( () -> {
      // previous contents of Plugin.onStop
      return F.Promise.pure(null);
    });
  }
}

在 Scala 中

import javax.inject.Inject

import scala.concurrent.Future

import play.api.inject.ApplicationLifecycle

trait MyComponent

class MyComponentImpl @Inject() (lifecycle: ApplicationLifecycle) extends MyComponent {
  // previous contents of Plugin.onStart
  lifecycle.addStopHook { () =>
    // previous contents of Plugin.onStop
    Future.successful(())
  }
}

§连接起来

现在是将您新创建的Module类添加到已启用模块集的时候了。这样做就像在您的配置文件中添加以下行一样简单

play.modules.enabled  += "my.module.MyModule"

如果您正在开发一个将被其他项目(包括子项目)使用的库,请在您的reference.conf文件中添加上述行(如果您还没有reference.conf,请创建一个并将其放在src/main/resources下)。否则,如果它是在最终的 Play 项目中,它应该在application.conf中。
如果它是一个最终的 Play 项目,您也可以创建一个名为Module的类,并将其放在根包(“app”目录)中。

注意:如果您正在开发一个库,强烈建议不要使用play.modules.disabled来禁用模块,因为它会导致模块被应用程序加载时出现非确定性结果(有关为什么不应该触碰play.modules.disabled的原因,请参阅此问题)。实际上,play.modules.disabled旨在供最终用户覆盖默认启用的模块。

§编译时 DI

通过定义一个Module类,您使您的组件可以使用运行时 DI 框架,例如 Google Guice 或 Spring。Scala 中流行的另一种方法是编译时 DI。为了使您的组件也适用于编译时 DI,请提供一个声明组件依赖关系的 Scala trait。以下是如何在运行示例中进行操作

import play.api.inject.ApplicationLifecycle

trait MyComponents {
  def applicationLifecycle: ApplicationLifecycle
  lazy val component: MyComponent = new MyComponentImpl(applicationLifecycle)
}

§删除您的Plugin类和play.plugins文件

此时,您应该已成功迁移您的代码,因此现在是删除您的Plugin类和play.plugins文件的时候了。后者通常位于您项目的conf文件夹中。

下一步:响应式流集成(实验性)


在此文档中发现错误?此页面的源代码可以在此处找到。在阅读文档指南后,请随时贡献拉取请求。有疑问或建议要分享?请访问我们的社区论坛,与社区开始对话。