文档

§Java 迁移指南

为了更好地融入 Java 8 生态系统,并允许 Play Java 用户在应用程序中更惯用地使用 Java,Play 已切换到使用许多 Java 8 类型,例如 CompletionStageFunction。Play 还为 EssentialActionEssentialFilterRouterBodyParserHttpRequestHandler 提供了新的 Java API。

§新的 Java API

为了适应在 Java 中编写过滤器和 HTTP 请求处理程序,特别是使用 HttpRequestHandler 接口,有一些 API 更改。如果您正在使用 Scala 编写这些组件,您仍然可以选择使用 Scala API。

§过滤器 API

在创建过滤器时,您最有可能使用 EssentialAction。您可以使用 Filter API 或更低级的 EssentialFilter API,该 API 在 EssentialAction 上运行。

§HttpRequestHandler 和 ActionCreator

HttpRequestHandler 实际上存在于 Play 2.4 中,但现在它具有不同的用途。createActionwrapAction 方法已移至名为 ActionCreator 的新接口,并在 HttpRequestHandler 中被弃用。这些方法仅应用于 Java 操作,用于拦截对控制器方法调用的请求,但并非所有请求。

在 2.5 中,HttpRequestHandler 的主要目的是在请求进来后立即提供一个处理程序。这现在与 Scala 实现一致,并为 Java 用户提供了一种在请求到达路由器之前拦截所有 HTTP 请求处理的方法。通常,HttpRequestHandler 会调用路由器以查找请求的操作,因此新的 API 允许您在请求到达路由器之前在 Java 中拦截该请求。

§在 Action 中使用 CompletionStage

Action 中使用 Java CompletionStage 时,必须显式地将 HTTP 执行上下文作为执行器提供,以确保 HTTP.Context 保持在范围内。如果您没有提供 HTTP 执行上下文,那么在调用 request() 或其他依赖于 Http.Context 的方法时,您将收到“此处没有可用的 HTTP 上下文”错误。

您可以通过依赖注入提供 play.libs.concurrent.HttpExecutionContext 实例

public class Application extends Controller {
    @Inject HttpExecutionContext ec;

    public CompletionStage<Result> index() {
        someCompletableFuture.supplyAsync(() -> { 
          // do something with request()
        }, ec.current());
    }
}

§用 Java 8 函数类型替换函数类型

Play 2.5 中的一项重大变化是尽可能使用标准 Java 8 类。所有函数类型都已替换为其 Java 8 对应类型,例如 F.Function1<A,R> 已替换为 java.util.function.Function<A,R>

迁移到 Java 8 类型应该能够更好地与其他 Java 库以及 Java 8 中的内置功能集成。

§如何迁移

步骤 1:将所有引用 Play 函数接口的代码更改为引用 Java 8 接口。

您需要更改明确提及 F.Function1 等类型的代码。例如

void myMethod(F.Callback0 block) { ... }

变为

void myMethod(Runnable block) { ... }

下表显示了所有更改

旧接口 新接口
F.Callback0 java.lang.Runnable
F.Callback<A> java.util.function.Consumer<A>
F.Callback2<A,B> java.util.function.BiConsumer<A,B>
F.Callback3<A,B,C> 在 Java 8 中没有对应类型,请考虑使用 akka.japi.function.Function3
F.Predicate<A> java.util.function.Predicate<A>
F.Function0<R> java.util.function.Supplier<R>
F.Function1<A,R> java.util.function.Function<A,R>
F.Function2<A,B,R> java.util.function.BiFunction<A,B,R>

步骤 2:修复由 lambda 表达式中抛出的已检查异常引起的任何错误。

与 Play 函数接口不同,Java 8 函数接口不允许抛出已检查异常。如果您的 lambda 表达式抛出已检查异常,则需要更改代码。(如果您没有抛出已检查异常,则可以保持代码不变。)

你可能会遇到很多编译错误,但让代码恢复正常运行非常容易。假设你的 Play 2.4 代码使用了一个 F.Callback0 lambda 来停止数据库

onClose(() -> {
    database.stop(); // <-- can throw an IOException
})

在 Play 2.5 中,onClose 方法已更改为接受 java.lang.Runnable 参数,而不是 F.Callback0。由于 Runnable 不能抛出检查异常,因此上面的代码在 Play 2.5 中无法编译。

要使代码编译,你可以更改 lambda 代码以捕获检查异常(IOException)并将其包装在未检查异常(RuntimeException)中。Runnable 可以抛出未检查异常,因此代码现在可以编译。

onClose(() -> {
    try {
        database.stop(); // <-- can throw an IOException
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
})

如果你不喜欢在代码中添加 try-catch 块,可以使用 Durian 库的 Errors 类来为你处理异常。

例如,你可以使用下面的代码获得与上面相同的行为,将检查异常转换为未检查异常

onClose(Errors.rethrow().wrap(database::stop));

Durian 还提供其他行为,例如 记录异常 或编写 你自己的异常处理程序。如果你想使用 Durian,你可以将其作为 项目中的依赖项,或者将源代码从 两个 复制到你的项目中。

§用 Java 8 的 CompletionStage 替换 F.Promise

使用 F.Promise 的 API 现在使用标准的 Java 8 CompletionStage 类。

§如何迁移

步骤 1:将所有返回 F.Promise 的代码更改为返回 CompletionStage。为了帮助迁移,F.Promise 也实现了 CompletionStage 接口,这意味着任何返回 Promise 的现有代码仍然可以从已迁移为使用 CompletionStage 的代码中调用。

步骤 2:用等效方法替换 F.Promise 中的相关静态方法(其中许多方法使用 play.libs.concurrent.Futures 帮助程序,或 CompletableFuture 上的静态方法)

F.Promise 方法 替代方法
Promise.wrap scala.compat.java8.FutureConverters.toJava
Promise.sequence Futures.sequence
Promise.timeout Futures.timeout
Promise.pure CompletableFuture.completedFuture
Promise.throwing 构造 CompletableFuture 并使用 completeExceptionally
Promise.promise CompletableFuture.supplyAsync
Promise.delayed Futures.delayed

步骤 3:CompletionStage 上的等效方法替换现有的实例方法

F.Promise CompletionStage
applyToEither
onRedeem thenAcceptAsync
map thenApplyAsync
transform handleAsync
zip thenCombine(并手动构造元组)
fallbackTo handleAsync 后跟 thenCompose(Function.identity())
recover exceptionally(或 handleAsyncHttpExecution#defaultContext(),如果您想捕获 Http.Context)。
recoverWith recover 相同,然后使用 .thenCompose(Function.identity())
onFailure whenCompleteAsync(如果需要,使用 HttpExecution#defaultContext()
flatMap thenComposeAsync(如果需要,使用 HttpExecution#defaultContext()
filter thenApplyAsync 并手动实现过滤器(如果需要,使用 HttpExecution#defaultContext()

这些迁移在 F.Promise 的 Javadoc 中有更详细的说明。

§用 Java 8 的 Optional 替换 F.Option

Play Java API 已转换为使用 Java 8 Optional 类,而不是 Play 的 F.Option 类型。F.Option 类型已被删除。

§如何迁移

Optional 替换使用 F.Option 的代码。这两种类型很相似,但它们的 API 不同,因此您需要更新您的代码。两者之间主要的区别是,虽然 F.Option 继承了 java.util.Collection,但 Optional 没有。

下面是一个简短的表格,可以帮助您迁移

F.Option Optional
F.Option.None() Optional.empty()
F.Option.Some(v) Optional.ofNullable(v)
o.isDefined() o.isPresent()
o.isEmpty() !o.isPresent()
o.get() o.get()
o.getOrElse(f) o.orElseGet(f)o.orElse(v)
o.map(f) o.map(f)

Optional 有很多组合器,所以我们强烈建议您学习它的 API,如果您还不熟悉它。

§线程局部属性

当与CompletionStage*Async回调一起使用时,线程局部属性(例如Http.ContextHttp.Session等)不再传递到不同的执行上下文。
更多信息请访问这里

§已弃用的静态 API

Play 2.5 中弃用了一些静态 API,转而使用依赖注入组件。使用静态全局状态不利于可测试性和模块化,建议您迁移到依赖注入以访问这些 API。您应该参考Play 2.4 迁移指南中的列表,以找到静态 API 的等效依赖注入组件。

下一步:加密迁移指南


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