§将插件迁移到模块
注意: 从 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
文件夹中。
下一步:响应式流集成(实验性)
在此文档中发现错误?此页面的源代码可以在此处找到。在阅读文档指南后,请随时贡献拉取请求。有疑问或建议要分享?请访问我们的社区论坛,与社区开始对话。