§Java 迁移指南
为了更好地融入 Java 8 生态系统,并允许 Play Java 用户在应用程序中更惯用地使用 Java,Play 已切换到使用许多 Java 8 类型,例如 CompletionStage 和 Function。Play 还为 EssentialAction、EssentialFilter、Router、BodyParser 和 HttpRequestHandler 提供了新的 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 中,但现在它具有不同的用途。createAction 和 wrapAction 方法已移至名为 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(或 handleAsync 带 HttpExecution#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.Context、Http.Session等)不再传递到不同的执行上下文。
更多信息请访问这里
§已弃用的静态 API
Play 2.5 中弃用了一些静态 API,转而使用依赖注入组件。使用静态全局状态不利于可测试性和模块化,建议您迁移到依赖注入以访问这些 API。您应该参考Play 2.4 迁移指南中的列表,以找到静态 API 的等效依赖注入组件。
下一步:加密迁移指南
发现文档中的错误?此页面的源代码可以在这里找到。阅读完文档指南后,请随时贡献拉取请求。有疑问或建议要分享?请访问我们的社区论坛,与社区进行交流。