文档

§Play 2.1 中的新功能?

§迁移到 Scala 2.10

Play 的整个运行时 API 已迁移到 Scala 2.10,允许您的应用程序参与此新语言版本提供的出色新功能。

同时,我们打破了构建系统 (sbt) 使用的 Scala 版本与运行时使用的 Scala 版本之间存在的依赖关系。这样,它使在 Scala 语言的实验性或不稳定分支上构建和测试 Play 变得更加容易。

§迁移到 scala.concurrent.Future

Scala 2.10 提供的最棒功能之一是用于在 Scala 中管理异步代码的新标准 scala.concurrent.Future 库。Play 现在基于此 API,其出色的异步 HTTP 和流式功能现在可以直接与使用相同 API 的任何其他库兼容。

它使使用 Play 与 Akka 或任何即将推出的使用此新 API 的异步数据存储驱动程序变得更加简单。

同时,我们致力于简化执行上下文模型,并通过提供一种简单的方法来为应用程序的每个部分选择用于运行代码的底层 ExecutionContext

§模块化

Play 项目已拆分为多个子项目,允许您选择项目所需的最小依赖项集。

您必须在以下列表中选择应用程序所需的精确可选依赖项集:

play 核心项目现在只有非常有限的外部依赖项,可以作为最小异步、高性能 HTTP 服务器使用,而无需其他组件。

§允许为您的项目提供更多模块化

为了允许在您自己的项目中进一步组合和重用组件,Play 2.1 支持子路由器组合。

例如,子项目可以定义自己的路由器组件,使用自己的命名空间,例如

conf/my.subproject.routes

GET   /                   my.subproject.controllers.Application.index

然后,您可以通过连接路由器将此组件集成到您的主应用程序中,例如

# The home page
GET   /                   controllers.Application.index

# Include a sub-project
->    /my-subproject      my.subproject.Routes

# The static assets
GET   /public/*file       controllers.Assets.at(file)

在配置中,在运行时,对 /my-subproject URL 的调用最终将调用 my.subproject.controllers.Application.index 操作。

注意:为了避免与主应用程序发生名称冲突,请始终确保在属于子项目的控制器类中定义一个子包(在本例中为 my.subproject)。您还需要确保子项目的 Assets 控制器在相同的命名空间中定义。

有关此功能的更多信息,请参阅 使用子项目

§Java API 中的 Http.Context 传播

在 Play 2.0 中,HTTP 上下文在异步回调期间丢失,因为这些代码片段在与处理 HTTP 请求的原始线程不同的线程上运行。

考虑

public static Result index() {
  return async(
    aServiceSomewhere.getData().map(new Function<String,Result>(data) {
      // Ouch! You try to access the request data in an asynchronous callback
      String user = session().get("user"); 
      return ok("Here is the result " + user + ": " + data);
    })
  );
}

此代码以前无法正常工作。如果您考虑底层的异步架构,这是有充分理由的,但对于 Java 开发人员来说,这确实令人惊讶。

我们最终找到了一种解决此问题的方法,并在跨越多个线程的堆栈上传播 Http.Context,因此此代码现在可以正常工作。

§Java API 的更好线程模型

在可变数据结构上运行异步代码时,如果您没有正确同步代码,则很可能会遇到一些竞争条件。由于 Play 推广高度异步和非阻塞代码,并且由于 Java 数据结构大多是可变的并且不是线程安全的,因此您的代码有责任处理同步问题。

考虑

public static Result index() {

  final HashMap<String,Integer> result = new HashMap<String,Integer>();

  aService.doSomethingAsync().map(new Function<String,String>(key) {
    Integer i = result.get(key);
    if(i != null) {
      result.set(key, i++);
    }
    return key;
  });

  aService.doSomethingElse().map(new Function<String,String>(key) {
    result.remove(key);
    return null;
  });

  ...
}

在此代码中,如果两个回调在两个不同的线程上同时运行,同时访问共享 result HashMap,则很可能会遇到竞争条件。由于底层 Java HashMap 的实现,结果可能是您的应用程序中出现一些伪死锁。

为了避免这些问题,我们决定在框架级别管理回调执行同步。Play 2.1 永远不会为同一个 Http.Context 并发运行 2 个回调。在这种情况下,所有回调执行将按顺序运行(虽然不能保证它们将在同一个线程上运行)。

§管理控制器类实例化

默认情况下,Play 静态地将 URL 绑定到控制器方法,也就是说,框架不会创建任何控制器实例,并且根据给定的 URL 调用适当的静态方法。但是,在某些情况下,您可能希望管理控制器创建,此时新的路由语法就派上用场了。

以 @ 开头的路由定义将由 Global::getControllerInstance 方法管理,因此,给定以下路由定义

GET     /                  @controllers.Application.index()

Play 将调用 getControllerInstance 方法,该方法将返回 controllers.Application 的实例(默认情况下,这是通过默认构造函数完成的)。因此,如果您希望通过依赖注入框架或手动管理控制器类实例化,您可以在应用程序的 Global 类中覆盖 getControllerInstance。

正如这个例子 所展示的,它允许将任何依赖注入框架(如 Spring)连接到您的 Play 应用程序。

§新的 Scala JSON API

新的 Scala JSON API 提供了很棒的功能,例如 JSON 树的转换和验证。请查看 Scala Json 组合器文档 中的新文档。

§新的过滤器 API 和 CSRF 保护

Play 2.1 提供了一个新的、功能强大的过滤器 API,允许以完全非阻塞的方式拦截 HTTP 请求或响应的每个部分。

为此,我们引入了一种新的更简单的类型来替换旧的 Action[A] 类型,称为 EssentialAction,它被定义为

RequestHeader => Iteratee[Array[Byte], Result]

因此,过滤器只需定义为

EssentialAction => EssentialAction

注意:为了兼容性,旧的 Action[A] 类型仍然可用。

filters 项目是标准 Play 发行版的一部分,它包含一组标准过滤器,例如提供针对 CSRF 安全问题的自动令牌管理的 CSRF

§RequireJS

在 Play 2.0 中,Javascript 的默认行为是使用 Google Closure 的 CommonJS 模块支持。在 2.1 中,这已更改为使用 requireJS

实际上,这意味着默认情况下,Play 仅在 stage、dist、start 模式下才会压缩和合并文件。在 dev 模式下,Play 将在客户端解析依赖项。

如果您希望使用此功能,则需要将您的模块添加到构建文件的 settings 块中

requireJs := "main.js"

有关此功能的更多信息,请参阅 RequireJS 文档页面

§内容协商

内容协商的支持已得到改进,例如,控制器现在会根据 Accept-Language 请求头值中设置的质量值自动选择最合适的语言。

编写支持同一资源的多种表示形式的 Web 服务也变得更加容易,并且可以根据 Accept 请求头值自动选择最佳表示形式。

val list = Action { implicit request =>
  val items = Item.findAll
  render {
    case Accepts.Html() => Ok(views.html.list(items))
    case Accepts.Json() => Ok(Json.toJson(items))
  }
}

有关更多信息,请参阅 ScalaJava 的内容协商文档页面。

下一步:迁移指南


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