尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Play Framework 2.5 迁移指南:从 2.4 升级的完整实战手册与源码级解析

发布时间:2026/9/23 23:30:45

资讯中心
01
ARTICLE

Play Framework 2.5 迁移指南:从 2.4 升级的完整实战手册与源码级解析

Play Framework 2.5 迁移指南:从 2.4 升级的完整实战手册与源码级解析
后端Web框架【免费下载链接】playframeworkThe Community Maintained High Velocity Web Framework For Java and Scala.项目地址https://gitcode.com/gh_mirrors/pl/playframework点击查看免费下载Play Framework 2.5 是一次以「去全局状态、拥抱 Java 8、统一流式处理」为核心的大版本升级涉及构建配置、Scala 版本、路由生成器、依赖注入、CSRF 安全策略、WS 客户端与 Netty 底层等多个层面。本文以官方 Migration25 指南为主体结合当前仓库中的实际源码与参考配置系统讲解从 Play 2.4 迁移到 2.5 的全部步骤、关键破坏性变更及其背后的实现原理帮助你在升级后获得可运行、可验证、贴近最佳实践的 Play 2.5 应用。提示如果你需要从更早的版本升级请先阅读 Play 2.4 Migration Guide。此外两个专题迁移指南提供了更细粒度的信息Streams Migration Guide迁移到 Akka Streams与 Java Migration GuideJava 应用迁移到原生 Java 8 类型。第一步升级构建配置升级 Play 2.4 → 2.5 的第一步是修改 sbt 构建使项目能在 sbt 中正常加载与运行。Play 版本升级在project/plugins.sbt中把 Play 的 sbt 插件版本升级到 2.5.xaddSbtPlugin(com.typesafe.play % sbt-plugin % 2.5.x)其中2.5.x的x表示你想要使用的次版本号例如2.5.0。sbt 升级到 0.13.11Play 2.5 仍然兼容 sbt 0.13.8但官方推荐升级到 0.13.11该版本包含大量改进与 bug 修复。将project/build.properties更新为sbt.version0.13.11Play Slick 升级如果你的项目使用 Play Slick需要升级到 2.0.0libraryDependencies com.typesafe.play %% play-slick % 2.0.0如果同时使用 evolutions 支持libraryDependencies Seq( com.typesafe.play %% play-slick % 2.0.0, com.typesafe.play %% play-slick-evolutions % 2.0.0 )Play Ebean 升级如果使用 Play Ebean升级其 sbt 插件addSbtPlugin(com.typesafe.sbt % sbt-play-ebean % 3.0.0)ScalaTest Play 升级如果使用 ScalaTest Play升级测试依赖libraryDependencies Seq( org.scalatestplus.play %% scalatestplus-play % 1.5.1 % test )Scala 2.10 支持终止全面迁移到 Scala 2.11Play 2.3 与 2.4 同时支持 Scala 2.10 和 2.11而 Play 2.5 终止了对 Scala 2.10 的支持仅支持 Scala 2.11。原因有二Play 2.5 内部大量使用scala-java8-compat库该库仅支持 Scala 2.11它提供了 Scala 与 Java 8 类型之间的转换例如 ScalaFuture与 JavaCompletionStage的互转。这个库对应用代码同样很有用。下一代 Play 计划支持 Scala 2.12先统一到 2.11 能让后续过渡更平滑。迁移步骤Scala 与 Java 用户都必须在 sbt 中配置 Scala 2.11——即使项目没有任何 Scala 代码Play 本身使用 Scala必须为其配置正确的 Scala 库。在 sbt 中设置scalaVersion即可scalaVersion : 2.11.8单项目构建可直接把该设置放在build.sbt中。多项目构建则必须为每个项目设置通常放在所有项目共享的公共设置里def common Seq( scalaVersion : 2.11.8 ) lazy val projectA (project in file(projectA)) .enablePlugins(PlayJava) .settings(common: _*) lazy val projectB (project in file(projectB)) .enablePlugins(PlayJava) .settings(common: _*)Logback 配置变更ColoredLevel 包路径迁移为了移除 Play 对 Logback 的硬编码依赖详见 Highlights25 的 Support for other logging frameworks 一节Logback 配置所用的一个类被移动到了新的包。迁移步骤更新logback*.xml中所有对旧类play.api.Logger$ColoredLevel的引用改为新的play.api.libs.logback.ColoredLevelconversionRule conversionWordcoloredLevel converterClassplay.api.libs.logback.ColoredLevel /当前仓库中该类的实现位于 ColoredLevel.scala它继承自 logback 的ClassicConverter把日志级别渲染成带颜色的小写文本TRACE 蓝色、DEBUG 青色、INFO 白色、WARN 黄色、ERROR 红色对应配置中的%coloredLevel转换词。如果你使用编译期依赖注入compile time DI需要把 application loader 中的Logger.configure(...)替换为LoggerConfigurator(context.environment.classLoader).foreach { _.configure(context.environment) }LoggerConfigurator是 Play 面向日志框架的抽象接口定义于 LoggerConfigurator.scala提供多个configure重载分别接收Environment、Configuration与属性映射。这一设计正是 2.5 支持任意 SLF4J 兼容日志框架的基础默认仍使用 Logback但可以通过disablePlugins(PlayLogback)移除再引入自定义框架的 SLF4J adapter详见 SettingsLogger 的 Using a Custom Logging Framework 一节。Play WS 升级到 AsyncHttpClient 2Play WS 底层升级为 AsyncHttpClient 2基于 Netty 4.0。大部分变化在底层但 AHC 2.0 的一些重大重构带来了 WS API 的破坏性变更AsyncHttpClientConfig被DefaultAsyncHttpClientConfig取代。allowPoolingConnection与allowSslConnectionPool在 AsyncHttpClient 中被合并为单一的keepAlive变量。因此play.ws.ning.allowPoolingConnection与play.ws.ning.allowSslConnectionPool不再有效配置它们会抛出异常。webSocketIdleTimeout已移除AhcWSClientConfig中不再提供。ioThreadMultiplier已移除AhcWSClientConfig中不再提供。FluentCaseInsensitiveStringsMap类被删除由 Netty 的HttpHeader类取代。Realm.AuthScheme.None已移除WSAuthScheme中不再提供。此外还有一些小变更为反映正确的 AsyncHttpClient 库名包play.api.libs.ws.ning更名为play.api.libs.ws.ahcNing*类更名为Ahc*AHC 配置前缀也从play.ws.ning改为play.ws.ahc例如play.ws.ning.maxConnectionsPerHost现在是play.ws.ahc.maxConnectionsPerHost。已废弃的接口play.libs.ws.WSRequestHolder被移除。play.libs.ws.play.WSRequest接口现在返回java.util.concurrent.CompletionStage而非F.Promise这是 Java 8 化改造的一部分参见 JavaMigration25。依赖Play.current或Play.application的静态方法被标记为废弃。2.5 之前 Play WS 会从 Content-Type 推断字符集并在请求头未设置字符集时自动追加到Content-Type头该行为曾引发混淆与 bug因此在 2.5.x 中Content-Type头不再自动携带推断出的字符集。如果显式设置Content-Type头则按原样生效。从当前仓库的源码结构看AHC 相关的客户端实现位于 play-ahc-ws 模块AhcWSClient的构造函数接收AhcWSClientConfig见 AhcWSClient.scalaAhcWSModule则通过AhcWSClientConfigParser从play.ws.ahc前缀的配置解析客户端参数见 AhcWSModule.scala。GlobalSettings 被废弃作为 Play 持续去除全局状态工作的一部分GlobalSettings与应用Global对象被标记为废弃。如何迁移离开GlobalSettings的详细说明参见 Play 2.4 迁移指南中的 GlobalSettings 章节。Play 2.4 起推荐的替代方案是将GlobalSettings的职责拆分为HttpErrorHandler、HttpRequestHandler与HttpFilters三个可注入组件对照表见 Migration24 的 Dependency Injected Components 一节。Plugins API 被移除Plugins API 在 Play 2.4 中被标记为废弃并在 Play 2.5 中正式移除。它已被 Play 的依赖注入与模块系统取代后者提供了更干净、更灵活的方式构建可复用组件。从插件迁移到依赖注入的细节参见 Play 2.4 迁移指南的 PluginsToModules 章节。路由默认使用 InjectedRoutesGeneratorPlay 2.5 起路由默认由依赖注入感知的InjectedRoutesGenerator生成而非假设控制器是单例对象的StaticRoutesGenerator。从源码看InjectedRoutesGenerator定义于 RoutesGenerator.scala其id为injected用于增量编译时判断路由生成器是否发生变化。它的核心逻辑是按控制器分组生成依赖描述符Dependency对每个路由若使用语法instantiate为 true则依赖jakarta.inject.Provider[控制器类]否则直接依赖控制器类见 RoutesGenerator.scala 第 126-140 行。也就是说前缀让路由器每次按需通过 Provider 获取控制器实例。在 RoutesCompiler.scala 中InjectedRoutesGenerator也是默认传入的生成器。如果代码中仍然使用object MyController这类静态控制器想恢复旧行为可以在build.sbt中添加routesGenerator : StaticRoutesGenerator如果使用Build.scala而不是build.sbt需要导入routesGenerator设置键import play.sbt.routes.RoutesCompiler.autoImport._使用静态控制器配合静态路由生成器并不算废弃但官方推荐迁移到使用依赖注入的类。静态控制器替换为依赖注入controllers.ExternalAssets现在是一个类不再提供静态等价物。controllers.Assets与controllers.Default也是类虽然静态等价物仍然存在但推荐使用类版本。当前仓库中ExternalAssets的实现位于 ExternalAssets.scala通过Inject构造注入专门用于从外部目录提供静态资源该控制器不适合在生产模式使用源码注释明确说明它会在生产模式自动禁用。迁移步骤推荐方案是让所有控制器都使用类。由于InjectedRoutesGenerator现在是默认路由生成器routes文件中的控制器会被当作类而非对象。如果仍有静态控制器可以使用StaticRoutesGenerator见上文并在routes文件中给路由加符号例如GET /assets/*file controllers.ExternalAssets.at(path /public, file)play.Play 与 play.api.Play 方法被废弃play.Play中以下方法已被废弃public static Application application()public static Mode mode()public static boolean isDev()public static boolean isProd()public static boolean isTest()同样play.api.Play中接受隐式Application并委托给 Application 的方法例如def classloader(implicit app: Application)也已被废弃。迁移步骤这些方法实际委托给play.Application或play.Environment——使用它们的代码应改为通过依赖注入获取相应组件。Play 内置组件的注入方式对照表参见 Play 2.4 迁移指南的 Dependency Injected Components 一节。例如下面的 Scala 控制器把 environment 与 configuration 注入进来class HomeController Inject() (environment: play.api.Environment, configuration: play.api.Configuration) extends Controller { def index Action { Ok(views.html.index(Your new application is ready.)) } def config Action { Ok(configuration.underlying.getString(some.config)) } def count Action { val num environment.resource(application.conf).toSeq.size Ok(num.toString) } }play.api.Environment的实现位于 Environment.scala它封装了应用部署的 rootPath、classLoader 与 mode 三个关注点并提供getFile、resource等基于 rootPath/classloader 的资源访问方法——这正是上面示例中environment.resource(application.conf)的底层能力。处理遗留组件通常你的组件不需要依赖整个应用但有时不得不处理要求传入 Application 的遗留组件。可以通过把应用注入到某个组件中来解决class FooController Inject() (appProvider: Provider[Application]) extends Controller { implicit lazy val app appProvider.get() def bar Action { Ok(Foo.bar(app)) } }注意此时通常应使用Provider[Application]以避免循环依赖。更好的做法是自建一个*Api类把静态方法包装成实例方法class FooApi Inject() (appProvider: Provider[Application]) { implicit lazy val app appProvider.get() def bar Foo.bar(app) def baz Foo.baz(app) }这样既能享受依赖注入带来的可测试性又能继续使用依赖全局状态的库。Content-Type 字符集变更在 Play 2.5 之前Play 会为某些未定义 charset 参数的 Content-Type特别是application/json与application/x-www-form-urlencoded自动追加charset参数。现在Content-Type默认不带 charset 发送无论是对 WS 发送请求还是从 Play action 返回响应。如果存在不符合规范、要求必须携带 charset 参数的客户端或服务端可以显式设置Content-Type头。Guice injector 与 Guice builder 变更默认情况下 Guice 可以通过代理环中的接口来解析循环依赖。由于循环依赖通常是代码坏味道且可以通过注入 Provider 打破循环Play 选择在默认 Guice injector 上禁用该特性。其他 DI 框架大多不具备此特性保留它也会在编写 Play 模块时引发问题。现在GuiceInjectorBuilder与GuiceApplicationBuilder提供了四个新方法来自定义 Guice 的注入行为disableCircularProxies禁用上面提到的通过代理接口解析循环依赖的行为如需允许代理使用disableCircularProxies(false)。requireExplicitBindings指示 injector 只注入在模块中显式绑定的类在测试中可用于校验绑定。requireAtInjectOnConstructors要求构造器带有Inject注解才能实例化类。requireExactBindingAnnotations禁用 Guice 中容易出错的行为——在注入Named(foo) Foo时用Named Foo的绑定来替代。这些方法的实现可以在 GuiceInjectorBuilder.scala 中找到它们通过BinderOption枚举DisableCircularProxies、RequireAtInjectOnConstructors、RequireExactBindingAnnotations、RequireExplicitBindings见第 400-403 行累积到 builder 中最终应用到 GuiceBinder。其中disableCircularProxies默认启用disable: Boolean true另外三个选项默认关闭与迁移指南的描述一致。CSRF 变更默认策略大幅收紧为了让 Play 的 CSRF 过滤器更能抵御浏览器插件漏洞与新扩展CSRF 过滤器的默认配置变得极为保守。主要变化包括不再黑名单化POST请求而是只白名单GET、HEAD、OPTIONS其余所有请求都需要 CSRF 检查——这意味着DELETE与PUT请求现在也会被检查。不再黑名单化application/x-www-form-urlencoded、multipart/form-data与text/plain而是所有 Content-Type包括无 Content-Type的请求都需要 CSRF 检查。一个直接后果是使用application/json的 AJAX 请求现在必须在Csrf-Token头中携带合法 CSRF token。基于无状态头的绕过机制如X-Requested-With默认被禁用。同时新增了一个配置项可针对携带特定头的请求绕过新的 CSRF 保护。该配置项默认对 Cookie 与 Authorization 头启用这样不使用 session 认证的 REST 客户端无需发送 CSRF token 也能正常工作。但需要注意由于该配置项会放行所有没有这些头的请求使用其他认证方案NTLM、TLS 客户端证书的应用将面临 CSRF 风险。这类应用应禁用该配置项使其无 cookie 的已认证请求也受到 CSRF 过滤器保护。最后还新增了一个选项对 CORS 过滤器信任的来源跳过 CSRF 检查。注意 CORS 过滤器必须位于 CSRF 过滤器之前才能生效。当前仓库的 CSRF 过滤器实现中该逻辑体现在 CSRFActions.scala 第 493 行当csrfConfig.bypassCorsTrustedOrigins为 true 且请求属性中带有 CORS 过滤器写入的Origin时跳过 CSRF 检查。相关配置类CSRFConfig定义于 csrf.scala其默认headerName为Csrf-Token、bypassCorsTrustedOrigins默认为true。play-filters-helpers 模块的 reference.conf 展示了 2.5 收紧后的默认配置play.filters.csrf { # 由 CORS 过滤器信任的来源可绕过 CSRF 检查 bypassCorsTrustedOrigins true header { # 接受 CSRF token 的请求头名称 name Csrf-Token # 必须存在才会执行 CSRF 检查的头默认为 Cookie 与 Authorization # 设为 null 或空对象则保护所有请求 protectHeaders { Cookie * Authorization * } # 存在即绕过的请求头默认为空 bypassHeaders {} } method { # 非空时不在此列表中的方法都会被检查 whiteList [GET, HEAD, OPTIONS] # 仅当 whiteList 为空时才使用 blackList [] } contentType { whiteList [] blackList [] } }如需恢复 Play 旧版的默认行为可在application.conf中添加如下配置play.filters.csrf { header { bypassHeaders { X-Requested-With * Csrf-Token nocheck } protectHeaders null } bypassCorsTrustedOrigins false method { whiteList [] blackList [POST] } contentType.blackList [application/x-www-form-urlencoded, multipart/form-data, text/plain] }获取 CSRF token此前可以在任意 action 中从 HTTP 请求获取 CSRF token。现在必须有 CSRF 过滤器或 CSRF actionCSRF.getToken才能工作。如果未使用过滤器可以在 Scala 中使用CSRFAddTokenaction、在 Java 中使用AddCSRFToken注解确保 session 中有 token。另外本版本修复了一个小 bug此前若 token 签名无效CSRF token 会变为空导致模板 helper 抛异常现在它会在同一请求内重新生成因此模板 helper 与CSRF.getToken仍能取到 token。Java 与 Scala 的 CSRF 完整文档分别见 JavaCsrf 与 ScalaCsrf。Crypto 被废弃拆分为专用签名器自 Play 1.x 起Play 就带有一个提供加密操作的Crypto对象Play 内部使用文档未正式提及仅在 scaladoc 中被称为「cryptographic utilities」。由于多种原因以便捷工具形式提供加密能力被证明不可行。在 2.5.x 中Play 专属功能被拆分为CookieSigner、CSRFTokenSigner与AESSigner三个 traitCrypto单例对象被废弃。迁移方法加密迁移取决于你的使用场景尤其是是否存在不安全的加密原语构造方式。简而言之尽量使用 Kalium否则使用 Tink 或直接使用 JCA。详细方案参见 Crypto Migration Guide。从当前仓库源码看拆分后的签名器实现在 CookieSigner.scala 与 CSRFTokenSigner.scalaCookieSigner负责对 cookie 内容计算消息认证码MACDefaultCookieSigner通过SecretConfiguration读取应用密钥play.crypto.secret其sign(message: String): String返回十六进制编码的签名trait 的文档明确警告它「不应作为通用 MAC 工具使用」。CSRFTokenSigner提供signToken、extractSignedToken、verifySignedToken与generateSignedToken等方法用于对 CSRF token 进行签名、提取与校验。CryptoMigration25 中还强调了几个关键安全结论不要用Crypto.sign或任何 HMAC 做密码哈希密码哈希应当慢而昂贵如 scrypt、bcrypt、PBKDF2Crypto.encryptAES默认使用 AES-CTR 模式只加密不认证存在可延展性malleability问题Play 内部对 session cookie 采用「先加密后 MAC」构造因此不受影响。更多细节可阅读 CryptoMigration25。Netty 4 升级Channel 选项配置键变更Netty 从 3.10 升级到 4.0一个直接后果是 Netty channel 选项的配置方式发生了变化。完整选项列表以 Netty 4.0 的ChannelOptionAPI 为准。迁移步骤将所有play.server.netty.option键修改为ChannelOption中定义的新键。常用映射如下旧键新键play.server.netty.option.backlogplay.server.netty.option.SO_BACKLOGplay.server.netty.option.child.keepAliveplay.server.netty.option.child.SO_KEEPALIVEplay.server.netty.option.child.tcpNoDelayplay.server.netty.option.child.TCP_NODELAY当前仓库中 play-netty-server 模块的 reference.conf 展示了这一命名体系的当前形态监听服务器 socket 的选项定义在option顶层接收客户端连接对应的 socket 选项以child.*前缀区分例如SO_BACKLOG、child.SO_KEEPALIVE、child.TCP_NODELAY并支持通过完整限定类名加#指定原生传输专属选项如io.netty.channel.ChannelOption#TCP_FASTOPEN。sendFile / sendPath / sendResource 的默认行为变更此前 Javaplay.mvc.StatusHeader与 Scalaplay.api.mvc.Results.Status的文件发送 API 在 inline 与 attachment 两种模式下表现不一致API方法默认值Scalaplay.api.mvc.Results.Status.sendResourceinlineScalaplay.api.mvc.Results.Status.sendPathattachmentScalaplay.api.mvc.Results.Status.sendFileattachmentJavaplay.mvc.StatusHeader.sendInputStreamnoneJavaplay.mvc.StatusHeader.sendResourceinlineJavaplay.mvc.StatusHeader.sendPathattachmentJavaplay.mvc.StatusHeader.sendFileinline也就是说旧版本在发送文件时混用了 inline 与 attachment 模式。现在发送文件、路径与资源时默认统一使用inline行为。当然你仍可通过这些方法的参数在两种模式间切换。当前仓库中 Scala 端的实现位于 Results.scalasendFile、sendPath、sendResource三个方法的inline: Boolean true参数默认值均已统一为true见第 622、642、666 行。实现细节上sendPath通过FileIO.fromPath流式读取文件sendResource通过 classloader 的getResourceAsStream打开资源后以流式发送二者都会结合Content-Disposition由Results.contentDispositionHeader(inline, name)生成与文件名的 MIME 类型推断来构建响应onClose回调可用于发送后清理临时文件等场景。迁移自检清单完成上述步骤后可以按以下清单快速自查迁移是否完整project/plugins.sbt中 sbt-plugin 版本为 2.5.xproject/build.properties中 sbt 版本为 0.13.11scalaVersion已设置为 2.11.x且多项目构建的每个 project 都生效logback*.xml中不再引用play.api.Logger$ColoredLevel编译期 DI 的 loader 使用LoggerConfigurator路由生成器为默认的InjectedRoutesGenerator控制器改为类并注入依赖仍有静态控制器时使用StaticRoutesGenerator且路由前加代码中不再调用废弃的play.Play/play.api.Play静态方法改用注入的Environment、Configuration等组件检查play.ws.ning.*配置是否全部迁移为play.ws.ahc.*CSRF 相关客户端尤其是application/jsonAJAX 与 REST 客户端确认携带Csrf-Token头或按需通过配置调整默认策略play.server.netty.option.*已映射为 Netty 4.0 的ChannelOption键名文件发送场景确认sendFile/sendPath/sendResource的 inline 默认值是否符合预期。Play 2.5 的迁移本质上是「拥抱 Java 8 与依赖注入、移除全局状态、统一流式处理」的一次整体演进本指南覆盖的构建升级、Scala 2.11 迁移、路由生成器切换、Guice 行为收紧、CSRF 策略保守化与 Netty 4 升级都是这一主线在具体 API 上的落地。对照上文各节与仓库源码逐项落实即可平稳完成升级并为后续版本如 Scala 2.12 支持打好基础。赞分享后端Web框架【免费下载链接】playframeworkThe Community Maintained High Velocity Web Framework For Java and Scala.项目地址https://gitcode.com/gh_mirrors/pl/playframework点击查看免费下载相关推荐Play 2.9 迁移指南从 Play 2.8 升级到 Play 2.9 的完整实战手册Play 2.9 迁移指南从 Play 2.8 升级到 Play 2.9 的完整实战手册 导读 本文基于 Play Framework 官方仓库中的《Play后端Web框架Play Framework Scala 3 迁移指南从 Scala 2 平滑升级的完整实战手册Play Framework Scala 3 迁移指南从 Scala 2 平滑升级的完整实战手册 Play FrameworkJava 与 Scala 的高后端Web框架Play Framework 2.8 迁移指南从 2.7 平滑升级的完整实践手册Play Framework 2.8 迁移指南从 2.7 平滑升级的完整实践手册 导读 本文以 Play Framework 官方迁移文档为主体结合本仓库后端Web框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。