文档

§编译时依赖注入

Play 默认提供了一种运行时依赖注入机制,即直到运行时才连接依赖关系的依赖注入。这种方法既有优点也有缺点,主要优点是减少了样板代码,主要缺点是应用程序的构建在编译时没有得到验证。

另一种方法是使用编译时依赖注入。最简单的方法是手动构建和连接依赖关系。还存在其他更高级的技术和工具,例如 Dagger。所有这些都可以轻松地在构造函数和手动连接之上实现,因此 Play 对编译时依赖注入的支持是通过提供公共构造函数和工厂方法作为 API 来提供的。

注意:如果您不熟悉编译时 DI 或一般的 DI,那么值得阅读 Adam Warski 的 Scala 中的 DI 指南,该指南讨论了编译时 DI 的一般概念。虽然这是针对 Scala 开发者的解释,但它也可以让您了解编译时注入的优势。

除了提供公共构造函数和工厂方法之外,Play 的所有开箱即用模块都提供了一些接口,这些接口实现了轻量级的蛋糕模式,以方便使用。这些接口建立在公共构造函数之上,完全是可选的。在某些应用程序中,它们可能不适合使用,但在许多应用程序中,它们将是连接 Play 提供的组件的非常方便的机制。这些接口遵循以 Components 结尾的特征名称命名约定,例如,DB API 的基于 HikariCP 的默认实现提供了一个名为 HikariCPComponents 的接口。

注意:当然,Java 在完全实现蛋糕模式方面有一些限制。例如,您不能在接口中拥有状态。

在下面的示例中,我们将展示如何使用内置组件帮助器接口手动连接 Play 应用程序。通过阅读提供的组件接口的源代码,将此方法调整为其他依赖注入技术应该很简单。

§应用程序入口点

每个在 JVM 上运行的应用程序都需要一个通过反射加载的入口点 - 即使您的应用程序自行启动,主类仍然通过反射加载,并且其主方法使用反射定位并调用。

在 Play 的开发模式下,Play 使用的 JVM 和 HTTP 服务器必须在应用程序重启之间保持运行。为了实现这一点,Play 提供了一个 ApplicationLoader 接口,您可以实现它。应用程序加载器在每次应用程序重新加载时都会被构造和调用,以加载应用程序。

此接口的 load 方法将应用程序加载器 Context 作为参数,其中包含 Play 应用程序所需的所有组件,这些组件比应用程序本身更长寿,并且不能由应用程序本身构造。其中一些组件专门用于在开发模式下提供功能,例如,源代码映射器允许 Play 错误处理程序呈现抛出异常的位置的源代码。

最简单的实现可以通过扩展 Play BuiltInComponentsFromContext 抽象类来提供。此类接受上下文,并基于该上下文提供所有内置组件。您只需要提供一个路由器,以便 Play 将请求路由到它。以下是使用空路由器以这种方式创建的最简单的应用程序

import play.Application;
import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.LoggerConfigurator;
import play.controllers.AssetsComponents;
import play.db.ConnectionPool;
import play.db.HikariCPComponents;
import play.filters.components.HttpFiltersComponents;
import play.mvc.Results;
import play.routing.Router;
import play.routing.RoutingDslComponentsFromContext;
public class MyComponents extends BuiltInComponentsFromContext implements HttpFiltersComponents {

  public MyComponents(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public Router router() {
    return Router.empty();
  }
}

然后是应用程序加载器

public class MyApplicationLoader implements ApplicationLoader {

  @Override
  public Application load(Context context) {
    return new MyComponents(context).application();
  }
}

要配置 Play 使用此应用程序加载器,请将 play.application.loader 属性配置为指向 application.conf 文件中的完全限定类名

play.application.loader=MyApplicationLoader

此外,如果您正在修改使用内置 Guice 模块的现有项目,您应该能够从 build.sbt 中的 libraryDependencies 中删除 guice

§提供路由器

要配置路由器,您有两个选择,使用 RoutingDsl 或生成的路由器。

§使用 RoutingDsl 提供路由器

为了使这更容易,Play 有一个 RoutingDslComponentsFromContext 类,它已经提供了一个 RoutingDsl 实例,该实例使用其他提供的组件创建

public class MyComponentsWithRouter extends RoutingDslComponentsFromContext
    implements HttpFiltersComponents {

  public MyComponentsWithRouter(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public Router router() {
    // routingDsl method is provided by RoutingDslComponentsFromContext
    return routingDsl().GET("/path").routingTo(request -> Results.ok("The content")).build();
  }
}

§使用生成的路由器

默认情况下,Play 将使用 注入的路由生成器。这将生成一个路由器,其构造函数接受来自路由文件中的每个控制器和包含的路由器,顺序与它们在路由文件中出现的顺序相同。路由器的构造函数还将以 play.api.http.HttpErrorHandlerplay.http.HttpErrorHandler 的 Scala 版本)作为第一个参数,用于处理参数绑定错误,以及一个前缀字符串作为最后一个参数。还将提供一个重载的构造函数,该构造函数将此默认设置为 "/"

以下路由

GET        /                    controllers.HomeController.index
GET        /assets/*file        controllers.Assets.at(path = "/public", file)

将生成一个路由器,该路由器接受 controllers.HomeControllercontrollers.Assets 以及任何其他具有声明路由的实例。要在实际应用程序中使用此路由器

public class MyComponentsWithGeneratedRouter extends BuiltInComponentsFromContext
    implements HttpFiltersComponents, AssetsComponents {

  public MyComponentsWithGeneratedRouter(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public Router router() {
    HomeController homeController = new HomeController();
    Assets assets =
        new Assets(scalaHttpErrorHandler(), assetsMetadata(), environment().asScala());
return new router.Routes(scalaHttpErrorHandler(), homeController,
    return new javaguide.dependencyinjection.Routes(
            scalaHttpErrorHandler(), homeController, assets)
        .asJava();
  }
}

§配置日志记录

为了在 Play 中正确配置日志记录,必须在返回应用程序之前运行 LoggerConfigurator。默认的 BuiltInComponentsFromContext 不会为您调用 LoggerConfigurator

此初始化代码必须添加到您的应用程序加载器中

import scala.jdk.javaapi.OptionConverters;
public class MyAppLoaderWithLoggerConfiguration implements ApplicationLoader {
  @Override
  public Application load(Context context) {

    LoggerConfigurator.apply(context.environment().classLoader())
        .ifPresent(
            loggerConfigurator ->
                loggerConfigurator.configure(context.environment(), context.initialConfig()));

    return new MyComponents(context).application();
  }
}

§使用其他组件

如前所述,Play 提供了许多用于连接其他组件的辅助特征。例如,如果您想使用数据库连接池,您可以将 HikariCPComponents 混合到您的组件蛋糕中,如下所示

public class MyComponentsWithDatabase extends BuiltInComponentsFromContext
    implements HikariCPComponents, HttpFiltersComponents {

  public MyComponentsWithDatabase(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public Router router() {
    return Router.empty();
  }

  public SomeComponent someComponent() {
    // connectionPool method is provided by HikariCPComponents
    return new SomeComponent(connectionPool());
  }
}

其他辅助特征也可用,例如 CSRFComponentsAhcWSComponents。提供组件的 Java 接口的完整列表是

下一步:应用程序设置


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