§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 的等效依赖注入组件。
下一步:加密迁移指南
发现文档中的错误?此页面的源代码可以在这里找到。阅读完文档指南后,请随时贡献拉取请求。有疑问或建议要分享?请访问我们的社区论坛,与社区进行交流。