文件

§Play 2.9 遷移指南

§如何遷移

在啟動 sbt 之前,請務必進行以下升級。

§Play 升級

project/plugins.sbt 中更新 Play 版本號碼

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.9.x")

其中 2.9.x 中的「x」是您要使用的 Play 次要版本,例如 2.9.0
查看 Play 次要版本的發行說明 發行版本

§sbt 升級

Play 2.9 僅支援 sbt 1.9 或更新版本。若要更新,請變更您的 project/build.properties,使其讀取

sbt.version=1.9.6

撰寫本文時,1.9.6 是 sbt 1.x 系列中的最新版本,您也可以使用更新的版本。查看 sbt 的發行說明 發行版本 以取得詳細資訊。

§最低要求的 Java 和 sbt 版本

Play 2.9 已取消對 Java 8 的支援,並至少需要 Java 11。其他支援的 Java 版本為 17 和 21。在升級到 Play 2.9 時,我們強烈建議考慮升級到至少 Java 17 LTS。值得注意的是,在即將推出的 Play 2.x 版本(版本 2.10)中,我們可能會停止支援 Java 11。

請注意,在 Java 17 和 Java 21 中使用自簽憑證繫結 HTTPS 埠可能會導致問題。有關此問題的更多詳細資訊,請參閱 "在 Java 17 和 Java 21 中產生自簽憑證失敗"

§已移除 Jetty ALPN 代理

由於 Java 11 是 Play 2.9 的最低要求 Java 版本,且支援開箱即用的 ALPN,因此 Jetty ALPN 代理 不再是絕對必要的。
如果您已將 -javaagent:jetty-alpn-agent-*.jar 旗標傳遞給您的 Play 應用程式(可能是透過 SBT_OPTS 環境變數),現在您必須移除該旗標。

§升級 Akka 和 Akka HTTP

如果您希望升級到 Akka 2.6 和 Akka HTTP 10.2 以上的版本,您可以在我們的 Play ScalaPlay Java 更新指南的協助下進行。我們也強烈建議您檢閱

§API 變更

Play 2.9 包含多項 API 變更。我們一如往常,遵循在移除現有 API 前先將其標示為不建議使用的政策。本節將詳細說明這些變更。

§Scala 2.12 支援已中止

Play 2.9 支援 Scala 2.13 和 Scala 3.3,但不再支援 2.12。Scala 3 需要更多遷移步驟,您可以在我們的 Scala 3 遷移指南 中找到這些步驟。

§在專案中設定 scalaVersion

Scala 和 Java 使用者都必須設定 sbt 以使用受支援的 Scala 版本。即使專案中沒有 Scala 程式碼,Play 本身也會使用 Scala,且必須設定為使用正確的 Scala 函式庫。

若要在 sbt 中設定 Scala 版本,只需設定 scalaVersion 鍵,例如

scalaVersion := "2.13.13"

Play 2.9 也支援 Scala 3

scalaVersion := "3.3.3"

重要的是要強調,Play 專門支援 Scala LTS(長期支援) 版本。因此,Scala 3.3 LTS 和後續 LTS 版本之間的任何 Scala 版本都不會受到 Play 的官方支援。不過,使用 Play 搭配此類 Scala 版本可能仍然可行。

如果您有一個單一專案建置,則此設定可以單獨放在 build.sbt 中。但是,如果您有一個多專案建置,則必須在每個專案中設定 Scala 版本設定。通常,在多專案建置中,您會有一些每個專案共用的設定,這是放置設定的最佳位置,例如

def commonSettings = Seq(
  scalaVersion := "2.13.13"
)

val projectA = (project in file("projectA"))
  .enablePlugins(PlayJava)
  .settings(commonSettings)

val projectB = (project in file("projectB"))
  .enablePlugins(PlayJava)
  .settings(commonSettings)

§重新命名為更一致的方法名稱

一些類別和方法已重新命名,以增強不同 API 之間的一致性,特別是新增、移除和清除資料的方法。
為了順利進行遷移,較舊的方法現已棄用,並將在未來版本中移除。

特徵中重新命名的函式 play.api.i18n.MessagesApi

已棄用的函式 新的函式
clearLang(result: Result) withoutLang(result: Result)

物件中重新命名的函式 play.api.mvc.Results

已棄用的函式 新的函式
clearingLang(result: Result) withoutLang(result: Result)

函式在 play.api.libs.typedmap.TypedMap 中重新命名

已棄用的函式 新的函式
+ updated
- removed

已重新命名下列類別

已棄用的類別 新的類別
HttpExecutionContext ClassLoaderExecutionContext
HttpExecution ClassLoaderExecution

§已移除已棄用的 API

Play 2.9 中移除了許多在早期版本中已棄用的 API。如果您仍在使用它們,建議您在升級到 Play 2.9 之前先遷移到新的 API。查看 Javadoc 和 Scaladoc 以取得遷移注意事項。另請參閱 Play 2.8 遷移指南 以取得更多資訊。

§變更 CSP 報告類型

根據 w3.org 規範,CSP 報告中有一些變更。自 Play 2.9 起,lineNumbercolumnNumber 欄位為 Long。如果您的實作依賴這些欄位為字串,Play 會回退將它們從字串解析為 Long,並僅在解析失敗時擲回錯誤。不過,建議使用數字類型,而非字串,因為這可能會在 CSP3 推出時變更。

§Request.asJava 現在總是會將主體包覆在 RequestBody

使用 asJava 方法將 Scala 要求轉換為 Java 要求時,產生的 Java 要求現在將始終將主體包裝在 Http.RequestBody 類別中。此變更可確保一致性,因為在 Play Java 中,要求主體始終包裝在此類別中。

先前,此轉換並未乾淨執行,且已轉換的 Java 要求可能會直接包含 Scala 要求主體,而未包裝在 Http.RequestBody 中。

但是,請務必注意,儘管將要求主體包裝在 RequestBody 類別中,但在將 Scala 要求轉換為 Java 要求時,主體本身並不會自動轉換為其 Java 等效項。例如,如果 Scala 要求包含 play.api.mvc.RawBuffer,則不會轉換為其 Java 等效項 play.mvc.Http.RawBuffer。類似地,Scala AnyContentAsEmpty 也不會轉換為 java.util.Optional.empty()(這是空主體的 Play Java 等效項)。因此,像 request.asJava.body().asRaw().asJson() 等輔助方法可能會無法如預期般運作。

若要擷取任何儲存的 Play Scala 主體物件,您可以使用 request.asJava.body().as(classOf[Object])

§從 Java Persistence API 轉換至 Jakarta Persistence API

為了最終支援 Hibernate ORM 6+ 和 EclipseLink 3+,Play 需要將其 JPA 模組 轉換至 Jakarta Persistence API。若要移轉您的程式碼,您必須先在 build.sbt 中的 libraryDependencies 中升級 Hibernate 或 EclipseLink。然後,請依照下列方式調整 Java 檔案中的匯入

import javax.persistence.*;

轉換為

import jakarta.persistence.*;

此外,請更新您的 persistence.xml,如下所示

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">
...

轉換為

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
             version="3.0">
...

此外,請透過將前綴 javax.persistence 替換為 jakarta.persistence 來更新所有組態參數。例如

<property name="javax.persistence.jdbc.driver" value="..."/>

轉換為

<property name="jakarta.persistence.jdbc.driver" value="..."/>

此外,我們已收到 報告 指出,如有需要,可能必須將組態中的提供者從

<provider>org.hibernate.ejb.HibernatePersistence</provider>

變更為

<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

才能確保 Hibernate 能與 Play 2.9 正常運作。

雖然使用了 version="3.0"persistence_3_0.xsd,但此 XML 宣告對於 最新的 Jakarta Persistence 3.1 而言是正確的。這是因為在 Jakarta Persistence 3.1 中,persistence.xml 架構保持不變。為了避免重複,3.0 架構會重新用於版本更新:https://jakarta.ee/xml/ns/persistence/

§JPA Bean Validation 目前不支援 Play 2.9

請務必注意,Bean Validation 目前不支援 Play 2.9。如需更多資訊,請參閱 這裡 提供的詳細資料。

§JNotify 監控服務不再可用

JNotifyFileWatchService 已從 play-file-watch 中移除,因此

play.dev.filewatch.FileWatchService.jnotify

不再可用於 PlayKeys.fileWatchService 的值。

§PlayKeys 中移除一些設定

設定 playOmnidocplayDocsNameplayDocsModuleplayDocsJar 已從 PlayKeys 中移除。這些設定不再使用,因為一段時間以來無法在本地執行 Play 文件。

§名稱空間變更:org.fluentleniumio.fluentlenium

FluentLenium 已將其名稱空間從 org.fluentlenium 遷移至 io.fluentlenium。因此,您可能需要相應地更新您的匯入。

§組態變更

本節列出組態中的變更和不建議使用事項。

§應用程式密碼強制執行最小長度

應用程式密碼 組態 play.http.secret.key 現在在所有模式(生產、開發和測試)中強制執行最小長度檢查。如果未達到最小長度,將擲回錯誤,導致組態無效並阻止應用程式啟動。

最小長度取決於用於簽署階段或快閃 Cookie 的演算法,可透過組態金鑰 play.http.session.jwt.signatureAlgorithmplay.http.flash.jwt.signatureAlgorithm 設定。預設情況下,兩個組態都使用 HS256 演算法,需要至少 256 位元(32 位元組)的密碼。對於 HS384,最小大小為 384 位元(48 位元組),對於 HS512,為 512 位元(64 位元組)。

當此錯誤發生時,它會提供詳細的解決資訊

Configuration error [
  The application secret is too short and does not have the recommended amount of entropy for algorithm HS256 defined at play.http.session.jwt.signatureAlgorithm.
  Current application secret bits: 248, minimal required bits for algorithm HS256: 256.
  To set the application secret, please read https://playframework.com/documentation/latest/ApplicationSecret
]

您可以透過確保密碼包含所需數量的位元/位元組來解決此錯誤。例如,您可以使用 head -c 32 /dev/urandom | base64 產生至少 32 位元組完全隨機輸入的密碼,或者您可以使用應用程式密碼產生器與 playGenerateSecretplayUpdateSecret

§新的 Logback 組態格式

從版本 1.3 開始,Logback 為其組態檔案使用新的正規格式。Play 已升級到最新的 Logback 版本,因此您應該將 Logback 組態檔案更新為此新格式。您可以輕鬆使用 Logback 團隊提供的 線上轉換器 執行此操作。使用您的 GitHub 帳戶登入,然後複製並貼上現有的 Logback 組態進行轉換。

儘管舊版組態格式仍可使用,但建議現在就升級,因為此程序很簡單。

此外,Play 特定的 coloredLevel 轉換器已不建議使用。Logback 已提供內建的著色樣式 一段時間了。因此,請從您的 Logback 組態檔案中移除以下行

<conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />

相反地,在您的模式中,將 %coloredLevel 替換為 %highlight(%-5level)

<!-- Deprecated: -->
<pattern>... %coloredLevel ...</pattern>

<!-- Recommended: -->
<pattern>... %highlight(%-5level) ...</pattern>

§已移除 play.akka.config 設定

在啟動 actor 系統時,Akka 會從提供的「根目錄」組態中的硬編碼 akka 前綴中查詢其設定。此行為是 Akka 的固有行為,不特定於 Play。

預設情況下,Play 指示 Akka 直接從應用程式組態根目錄路徑載入其 actor 系統設定。因此,您通常會在 application.confakka.* 名稱空間下組態 Play 的 actor 系統。

在 Play 2.9 之前,您可以選擇使用 play.akka.config 組態來指定 Play 擷取其 Akka 設定的替代位置。如果您打算將 akka.* 設定用於單獨的 Akka actor 系統,這會很有用。但是,此組態已因兩個主要原因而移除

  1. 缺乏文件:play.akka.config 設定沒有充分的文件。文件未說明,即使您設定 play.akka.config = "my-akka",仍會將組態根目錄路徑中的 akka.* 設定載入為備用。這表示對 akka.* 組態所做的任何變更也會影響在 my-akka.akka.* 中定義的 actor 系統。因此,這兩個 actor 系統無法獨立運作,而 play.akka.config 所暗示的承諾(讓 akka.* 專門用於另一個 Akka actor 系統)也無法實現。

  2. 組態預期:Play 預期 Actor 系統組態將駐留在 akka.* 名稱空間中,並在該前綴中設定各種組態以確保順利運作。如果您要為 Play 使用完全不同的 Actor 系統,您的應用程式可能會停止正常運作。

由於這些考量,從 Play 2.9 開始,akka.* 前綴專門指定給 Play 的 Akka 組態,且無法變更。您仍然可以實作自己的 Actor 系統,但務必確保它們不會從根路徑中的 Play akka 前綴讀取其組態。

§直接定義在 akka.* 組態中的 Dispatcher 將不再自動載入

移除 play.akka.config 組態的副作用是,不再能夠直接在 akka.* 組態金鑰中定義 Dispatcher,因為它們不再會自動載入到預設 Actor 系統中。相反地,您會遇到類似這樣的例外狀況

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[ConfigurationException: Dispatcher [my-context] not configured]]
...
Caused by: akka.ConfigurationException: Dispatcher [my-context] not configured
...

您現在必須

實際上,Play 2.9 及更新版本的行為是正確的:不應自動從 akka.* 金鑰讀取 Dispatcher。這在純 Akka 應用程式中也無法運作。能夠將 Dispatcher 放在 akka.* 金鑰中只是損壞的 play.akka.config 實作的 Play 特定副作用。

§預設值變更

Play 使用的幾個預設值已變更,這可能會影響您的應用程式。本節概述這些預設變更。

§自簽憑證產生在 Java 17 和 Java 21 中失敗

在繫結 HTTPS 埠的過程中,Play 通常會產生自簽憑證作為預設程序。然而,在使用 Java 17 時,此操作可能會導致以下例外

 java.lang.IllegalAccessError: class com.typesafe.sslconfig.ssl.FakeKeyStore$ (in unnamed module @0x68c8dd0e) cannot access class sun.security.x509.X509CertInfo (in module java.base) because module java.base does not export sun.security.x509 to unnamed module @0x68c8dd0e (FakeKeyStore.scala:89)
com.typesafe.sslconfig.ssl.FakeKeyStore$.createSelfSignedCertificate(FakeKeyStore.scala:89)
com.typesafe.sslconfig.ssl.FakeKeyStore$.generateKeyStore(FakeKeyStore.scala:79)
play.core.server.SelfSigned$.x$1$lzycompute(SelfSigned.scala:24)
play.core.server.SelfSigned$.x$1(SelfSigned.scala:23)
play.core.server.SelfSigned$.sslContext$lzycompute(SelfSigned.scala:23)
play.core.server.SelfSigned$.sslContext(SelfSigned.scala:23)
play.core.server.SelfSignedSSLEngineProvider.sslContext(SelfSigned.scala:36)
...

為了在標準的 Play 應用程式設定中迴避此例外,測試的 HTTPS 埠繫結已停用作為預設設定。值得注意的是,測試中 HTTPS 埠的取消繫結是一種回歸到舊行為,因為 HTTPS 埠繫結在早期的 Play 版本中被停用後,我們無意中啟用了它。

如果您希望在使用 Play 產生的自簽憑證時對 HTTPS 埠進行測試,則需要針對 Java 17 進行解決方法。這涉及將 --add-export 標記新增到 Java 17 命令列。此外,在測試執行期間,建立新的 Java 虛擬機器 (JVM) 執行個體至關重要

Test / javaOptions += "--add-exports=java.base/sun.security.x509=ALL-UNNAMED"
// Test / fork := true // This is the default behavior in Play; a reminder in case the setting was changed to false previously

或者,您可以設定 JAVA_TOOL_OPTIONS 環境變數

export JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS --add-exports=java.base/sun.security.x509=ALL-UNNAMED";

不幸的是,上述解決方法不足以適用於 Java 21。在這種情況下,您可能會遇到以下例外

 java.lang.NoSuchMethodError: 'void sun.security.x509.X509CertInfo.set(java.lang.String, java.lang.Object)' (FakeKeyStore.scala:92)
com.typesafe.sslconfig.ssl.FakeKeyStore$.createSelfSignedCertificate(FakeKeyStore.scala:92)
com.typesafe.sslconfig.ssl.FakeKeyStore$.generateKeyStore(FakeKeyStore.scala:79)
play.core.server.SelfSigned$.x$1$lzycompute(SelfSigned.scala:24)
play.core.server.SelfSigned$.x$1(SelfSigned.scala:23)
play.core.server.SelfSigned$.sslContext$lzycompute(SelfSigned.scala:23)
play.core.server.SelfSigned$.sslContext(SelfSigned.scala:23)
play.core.server.SelfSignedSSLEngineProvider.sslContext(SelfSigned.scala:36)
...

目前,在 Play 中使用 Java 21 來產生自簽憑證是不可行的。建議繼續使用 Java 17 來執行此目的。您可以透過以下 GitHub 連結來監控此問題的解決方案。

通常,自簽憑證不用於提供網站服務。有關更全面的 HTTPS 設定資訊,請參閱 「設定 HTTPS」「在生產環境中使用 Play」

§處理 AnyContent 主體剖析器的空主體的變更

如果您使用 AnyContent 主體剖析器,例如 Play Scala 中的 parse.anyContent 或 Play Java 中的 @BodyParser.Of(BodyParser.AnyContent.class),請務必注意當主體剖析程序產生一個空主體時,行為已更新。在 Play Scala 中,請求主體現在會指派給 AnyContentAsEmpty,而在 Play Java 中,它會設定為 Optional.empty()
先前,一個空主體會根據 Content-Type 標頭關聯到一個特定的「空」類型。例如,如果 Play 將一個空主體剖析為一個原始緩衝區,請求會包含一個大小為零的 RawBuffer 物件。

§處理不正確的百分比編碼 URI 字元

自 akka-http 10.2.0 以來,不正確的百分比編碼 URI 字元會立即被 akka-http 視為 400 錯誤請求。這種行為不會讓 Play 有機會處理錯誤。為了確保兩個 HTTP 後端之間的相容性,我們讓 netty 以相同的方式運作,也就是在剖析不正確的百分比編碼字元時不會透過錯誤處理程式。這種方法旨在最大化相容性,以防一個後端被另一個後端取代。

§Caffeine 有一個由 play.cache.caffeine.defaults.maximum-size 定義的新預設值。

如果您的應用程式隨著時間推移而將許多項目新增到快取中,使用基於 Caffeine 的快取時,JVM 最終可能會因 記憶體不足 錯誤而崩潰。我們將 Caffeine 組態的專案預設值定義為最大大小 10,000。

§Caffeine 現在在內部使用 Play 的預設調度程式

Caffeine 內部預設使用的調度器現在是 Play 的預設調度器。在 Play 2.9.0 之前,使用的是 Java 的共用池 (ForkJoinPool.commonPool())。
您可以透過設定檔 play.cache.dispatcher 來變更使用的調度器,甚至可以為不同的快取設定不同的執行器,如 Scala 快取 APIJava 快取 API 中所述。

§測試伺服器現在使用隨機埠

在使用 Play JavaPlay Scala specs2ScalaTest + Play 撰寫功能測試時,如果您沒有明確指定測試伺服器執行的埠,它現在會預設使用隨機埠,而不是之前的預設值,也就是埠 19001。這項變更旨在促進並行執行測試。除了提供明確的埠之外,您也可以設定系統屬性 testserver.port (例如,設定為 19001 以啟用之前的行為)。

在使用隨機埠時,重要的是在測試中擷取正確的埠。您現在可以使用新推出的方法 runningWithPort(TestServer(...))(port => block(..)),而不是使用 running(TestServer(...))(block(..)),它會提供實際使用的埠作為參數。

§測試伺服器預設不再繫結 HTTPS 埠

除了我們在前面章節討論的新隨機埠行為,測試伺服器預設不再繫結 HTTPS 埠。不過,你可以透過傳遞 testserver.httpsport 系統屬性來重新啟用此行為。如果 HTTPS 埠已繫結,將會使用自簽憑證。

§相依圖表變更

play-jdbc-api 工件不再相依於 play 核心工件。

§已更新函式庫

Play 2.9 升級了以下我們自己的函式庫

相依項 轉換為
Play File Watch 1.1.16 1.2.0
Play JSON 2.8.2 2.10.0
Play WS 2.1.11 2.2.0
Twirl 1.5.1 1.6.0

除了更新我們自己的函式庫至較新版本之外,許多其他重要的第三方相依項也已更新至最新版本

相依項 轉換為
Akka HTTP 10.1.15 10.2.10
Guice 4.2.3 6.0.0 使用 guice
HikariCP 3.4.5 5.0.1 使用 jdbc
scala-xml 1.3.1 2.2.0
Jackson 和 Jackson Module Scala 2.11.4 2.14.3
SBT Native Packager 1.5.2 1.9.16
Logback 1.2.12 1.4.11
SLF4J API 1.7.36 2.0.7
Caffeine 2.8.8 3.1.7 使用 caffeine
sbt-web 1.4.4 1.5.0
sbt-js-engine 1.2.3 1.3.0
Guava 30.1.1 32.1.2
Lightbend SSL Config 0.4.3 0.6.1
Java JSON Web Token (JJWT) 0.9.1 0.11.5
TypeTools 0.5.0 0.6.3 Java 路由 DSL 使用
FluentLenium 3.7.1 6.0.0 現在為 io.fluentlenium
Selenium 3.141.59 4.11.0
Selenium HtmlUnitDriver 2.36.0 4.11.0 現在與 Selenium 相同版本
specs2 4.8.3 4.20.2
JUnit Interface 0.11 0.13.3
Joda-Time 2.10.14 2.12.5 使用 jodaForms
Hibernate Validator 6.1.7 6.2.5.Final 使用 PlayJava 外掛時

§已變更 groupId

我們現在在 com.typesafe.play 名稱空間下發佈所有 Play 工件和函式庫。因此,我們必須對下列函式庫進行調整

工件 舊的 groupId 新的 groupId
play-file-watch com.lightbend.play com.typesafe.play
sbt-twirl com.typesafe.sbt com.typesafe.play

§已變更的工件名稱

為了確保所有工件命名一致,每個名稱都以 play- 為字首,我們已對其餘的 artifactId 進行調整

舊的 artifactId 新的 artifactId
build-link play-build-link
filters-helpers play-filters-helpers
routes-compiler play-routes-compiler
run-support play-run-support

§已移除的函式庫

下列相依性已從 Play 中移除

下一步:Scala 3 遷移指南


在此文件發現錯誤?此頁面的原始程式碼可以在 這裡 找到。在閱讀 文件指南 後,請隨時提出拉取請求。有問題或建議要分享?請前往 我們的社群論壇 與社群展開對話。