尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Play 2.3 迁移指南:从 Play 2.2 平滑升级到 2.3 的完整实战手册
后端Web框架【免费下载链接】playframeworkThe Community Maintained High Velocity Web Framework For Java and Scala.项目地址https://gitcode.com/gh_mirrors/pl/playframework点击查看免费下载本文是 Play Framework 官方 2.2 → 2.3 迁移指南的深度解读与仓库源码佐证版。Play 2.3 是一次里程碑式的内部重构构建系统全面切换到 sbt 0.13.5 的 Auto Plugins 机制play命令被activator取代Html/CSS/JavaScript 资产处理被整体抽取为独立的 sbt-web 插件生态模板引擎独立为 TwirlWS 客户端变为可选库结果类型Result体系完成收尾重构。阅读本文后你将掌握从构建定义、资产管线、模板、WS、Anorm 到测试基类与 Session 配置的完整迁移路径并能在当前仓库中逐一对证每一项变更的源码实现。提示如果你需要从更早的版本迁移请先阅读 Play 2.2 迁移指南再按本文步骤升级到 2.3。Activator从play命令到activator命令Play 2.3 中最直观的变化是命令行入口原来的play命令被activator命令取代Play 全面转向使用 Activator一个基于 sbt 的增强启动器。Activator 命令原来play命令提供的所有能力在activator命令中全部保留activator new创建新项目参见 Creating a new applicationactivator进入交互式控制台参见 Using the Play consoleactivator ui新增命令启动一个 Web 图形用户界面。需要特别说明的是新的activator命令和旧的play命令本质上都是 sbt 的包装器。如果你更习惯底层工具完全可以跳过 Activator 直接使用sbt命令。不过直接使用 sbt 会丢失 Activator 的部分独有特性例如项目模板activator new和 Web UIactivator ui。sbt 与 Activator 都支持全部常规控制台命令如test和run。Activator 发行版Play 以 Activator 发行版的形式发布该发行版内含 Play 的全部依赖可从 Play 官方下载页面获取。两种获取方式下载完整 Activator 发行版包含 Play 所有依赖开箱即用从 Activator 站点下载约 1MB 的 mini 版mini 发行版它只在需要时才按需下载依赖。由于 Activator 是 sbt 的包装器你同样可以直接下载并使用 sbt。构建系统变更sbt 0.13.5 与 Auto PluginsPlay 2.3 的构建体系发生了自 2.0 以来最大规模的重构涉及 sbt 版本、插件机制、Play 插件启用方式与 Scala 版本声明四个层面。升级 sbt 版本Play 2.3 基于 sbt 0.13.5。升级现有项目时将project/build.properties修改为sbt.version0.13.5升级 Play 插件版本在project/plugins.sbt中修改 Play 插件版本addSbtPlugin(com.typesafe.play % sbt-plugin % 2.3.XXX)其中2.3.XXX是你想使用的具体 Play 版本号。此外你还需按下文sbt-web一节的说明添加若干 sbt-web 插件。Auto Plugins 与插件启用方式sbt 0.13.5 引入了名为 auto plugins 的新特性它改变了插件在构建中的启用语义插件仍然像以前一样在project目录通常是plugins.sbt中声明变化在于插件现在可以声明自己对其他插件的依赖以及它在什么条件下被某个构建启用在 auto plugins 之前添加到构建中的插件总是对所有模块生效现在插件可以对指定模块选择性启用。这意味着仅仅声明addSbtPlugin可能不再足以让插件生效对那些使用了 auto plugin 机制的插件而言。这是一件好事——你可以按需选择项目中的哪些模块启用哪些插件例如lazy val root (project in file(.)).enablePlugins(SbtWeb)上面的例子把SbtWeb添加到了构建的根项目。SbtWeb是一类插件的 root 插件如果同时通过addSbtPlugin添加了sbt-less-plugin那么只要SbtWeb被启用该插件也会随之自动启用。Play 自身也改用了 auto plugin 机制。Play 2.2 中通过playJavaSettings/playScalaSettings启用插件的做法已被移除取而代之的是lazy val root (project in file(.)).enablePlugins(PlayJava)或Scala 项目lazy val root (project in file(.)).enablePlugins(PlayScala)如果你以前使用play.Project例如一个 Scala 项目object ApplicationBuild extends Build { val appName myproject val appVersion 1.0-SNAPSHOT val appDependencies Seq() val main play.Project(appName, appVersion, appDependencies).settings( ) }……那么可以改用原生 sbt 的等价写法object ApplicationBuild extends Build { val appName myproject val appVersion 1.0-SNAPSHOT val appDependencies Seq() val main Project(appName, file(.)).enablePlugins(play.PlayScala).settings( version : appVersion, libraryDependencies appDependencies ) }切换到上述风格后插件一旦启用其设置会被自动导入不再需要手动.settings(...)导入。PlayKeysPlay 提供的 sbt key 需限定引用Play 提供的 key 现在必须通过PlayKeys对象引用。例如引用playVersion时要么先导入import PlayKeys._要么用全限定名PlayKeys.playVersion。如果不使用.sbt文件即用完整 Scala 语法描述构建可以在作用域内引入PlayKeysimport play.Play.autoImport._ import PlayKeys._显式声明 scalaVersionPlay 2.3 同时支持 Scala 2.11 和 Scala 2.10。此前 Play 插件会自动为你设置scalaVersion现在需要你明确指定。在build.sbt或Build.scala中加入 Scala 版本使用 Scala 2.11scalaVersion : 2.11.1使用 Scala 2.10scalaVersion : 2.10.4sbt-web资产处理全面外置Play 2.3 最大的新特性是引入 sbt-web它把 Html、CSS 与 JavaScript 相关的处理能力从 Play 核心中剥离形成一族纯粹的 sbt 插件。这带来两大好处Play 对 Html/CSS/JavaScript 的立场不再那么固执己见sbt-web 可以拥有自己的社区与 Play 并行发展。此外sbt-web 插件可以在 JVM 内通过 Trireme 运行也可以原生使用 Node.js 运行。需要注意sbt-web 是开发期环境不参与 Play 应用的运行期执行。Trireme 是默认引擎如果你安装了 Node.js并希望构建更快可以通过 sbt 的SBT_OPTS环境变量指定系统属性来切换export SBT_OPTS$SBT_OPTS -Dsbt.jse.engineTypeNodesbt-web 的一个特性是它不关心你的目录叫javascripts还是stylesheets只要文件扩展名匹配就会被从app/assets目录中过滤出来参与处理。资产目录全部从 public 提供sbt-web 的一个细节变化是所有资产都从public目录提供。如果你此前将资产放在public之外例如使用过playAssetsDirectories设置playAssetsDirectories baseDirectory / foo那么现在应改为unmanagedResourceDirectories in Assets baseDirectory.value / foo但要注意放在那里的文件会被聚合进最终的 public 目录。这意味着public/a.js会被foo/a.js覆盖。另一种做法是在项目的 public 目录下使用子目录来做命名空间隔离。sbt-web 相关组件与版本清单以下是 Play 2.3 发布时点全部 sbt-web 相关组件及其版本Libraries库com.typesafe %% webdriver % 1.0.0 com.typesafe %% jse % 1.0.0 com.typesafe %% npm % 1.0.0sbt plugins插件com.typesafe.sbt % sbt-web % 1.0.0 com.typesafe.sbt % sbt-webdriver % 1.0.0 com.typesafe.sbt % sbt-js-engine % 1.0.0 com.typesafe.sbt % sbt-coffeescript % 1.0.0 com.typesafe.sbt % sbt-digest % 1.0.0 com.typesafe.sbt % sbt-gzip % 1.0.0 com.typesafe.sbt % sbt-less % 1.0.0 com.typesafe.sbt % sbt-jshint % 1.0.0 com.typesafe.sbt % sbt-mocha % 1.0.0 com.typesafe.sbt % sbt-rjs % 1.0.1WebJars前端依赖的声明式管理WebJars 在 Play 2.3 的资产供给中扮演了重要角色。例如声明使用流行的 Bootstrap 库只需在构建文件中加入依赖libraryDependencies org.webjars % bootstrap % 3.2.0WebJars 会被自动解压到 public 资产相对路径下的lib文件夹中。例如声明了对 RequireJs 的依赖后可以在视图中这样引用script>addSbtPlugin(com.typesafe.sbt % sbt-coffeescript % 1.0.0)CoffeeScript 的选项相比 2.2 有所变化新选项为sourceMap为真时生成 sourceMap 文件默认true。CoffeeScriptKeys.sourceMap : truebare为真时生成不带顶层函数安全包装的 JavaScript默认false。CoffeeScriptKeys.bare : falseLESS必须在plugins.sbt中显式声明插件addSbtPlugin(com.typesafe.sbt % sbt-less % 1.0.0)入口点现在通过过滤器filter声明。例如声明需要编译foo.less和bar.lessincludeFilter in (Assets, LessKeys.less) : foo.less | bar.less如果你以前使用下划线开头的 less 文件被忽略、其余全部编译的行为则使用以下过滤器组合includeFilter in (Assets, LessKeys.less) : *.less excludeFilter in (Assets, LessKeys.less) : _*.less与 Play 2.2 不同sbt-less 插件允许任何用户下载原始 LESS 源文件和生成的 source map方便在现代浏览器中调试——该特性在生产模式下同样开启。sbt-less 插件的选项如下OptionDescriptioncleancss使用 clean-css 压缩输出。cleancssOptions向 clean css 传递选项使用 https://github.com/GoalSmashers/clean-css 的 CLI 参数。colorLESS 输出是否着色。compress通过移除部分空白来压缩输出。ieCompat进行 IE 兼容性检查。insecure允许从不安全的 https 主机导入。maxLineLen最大行长度。optimization设置解析器的优化级别。relativeUrls将相对 URL 重写为相对于基准 less 文件。rootpath为相对导入和 url 中的 url 重写设置 rootpath。silent抑制错误消息输出。sourceMap输出 v3 sourcemap。sourceMapFileInlinesource map 是否内嵌到输出文件中。sourceMapLessInline是否将 less 代码内嵌到 source map 中。sourceMapRootpath将该路径附加到 sourcemap 文件名和 less 文件路径上。strictImports导入是否严格。strictMath要求使用括号。该选项未来可能默认为 true 并被移除。strictUnits所有单位是否严格或允许混合单位。verbose输出详细信息。Closure Compiler 被 JSHint 与 UglifyJS 2 取代Closure Compiler 已被替换。它的两个重要职能——校验 JavaScript 与压缩 JavaScript——被分别拆分到 JSHint 和 UglifyJS 2 中。使用 JSHint 需在plugins.sbt中声明addSbtPlugin(com.typesafe.sbt % sbt-jshint % 1.0.0)选项遵循 JSHint 官方网站的约定并共享同一组默认值。设置方式在项目根目录提供.jshintrc文件如果项目根目录没有该文件则会在你的主目录中查找.jshintrc。此行为可通过插件的JshintKeys.config设置覆盖它用于指定配置文件的位置。UglifyJS 2 目前经由 RequireJS 插件见下提供未来计划为不使用 RequireJS 的场景提供独立的 UglifyJS 2 插件。RequireJSrjs全新的资产管线优化器RequireJS Optimizerrjs被整体替换为更易用的新实现它属于 sbt-web 的资产管线asset pipeline功能。与旧版每次构建都执行不同新 rjs仅在通过stage或dist任务生成发行包时才被调用。在plugins.sbt中声明插件addSbtPlugin(com.typesafe.sbt % sbt-rjs % 1.0.1)将插件加入资产管线pipelineStages : Seq(rjs)官方同时推荐把 sbt-digest 和 sbt-gzip 也纳入管线sbt-digest 为 Play 的资产控制器提供文件指纹fingerprint能力便于对资产做远期缓存far-future cachingsbt-gzip 生成资产的 gzip 版本资产控制器在请求时会优先返回 gzip 版本。此时plugins.sbt为addSbtPlugin(com.typesafe.sbt % sbt-digest % 1.0.0) addSbtPlugin(com.typesafe.sbt % sbt-gzip % 1.0.0)管线配置变为pipelineStages : Seq(rjs, digest, gzip)管线的顺序很重要应先优化文件再生成摘要最后对所有产物生成 gzip 版本。RequireJS 优化的选项已完全改变新选项如下OptionDescriptionappBuildProfile项目构建 profile 的内容。appDir存放应用 js 文件的顶层目录。实际上就是 rjs 读取的源文件夹。baseUrl相对于 assets 或 public 文件夹、存放 js 文件的目录。默认取 js、javascripts 或 .前两者都找不到时取 .。buildProfile在 appBuildProfile 提供的默认值之外附加的 build profile key - value 设置这里的设置也会替换任何默认值。dir默认情况下所有模块都相对于该路径定位实际上是 rjs 的目标目录。generateSourceMaps默认生成 source maps。mainConfig默认使用 main 作为配置模块。mainConfigFile上述模块的完整路径。mainModule默认使用 main 作为模块。modules模块的 json 数组。optimize优化器名称默认 uglify2。paths模块 id 到构建路径生产路径二元组的 RequireJS 路径映射。默认情况下所有 WebJar 库都从 CDN 提供其映射可以在这里找到除非 cdn 被设为 None。preserveLicenseComments是否保留注释。鉴于 source maps默认 false参见 requirejs.org 的 sourcemapcomments 错误说明。webJarCdns用于定位 WebJars 的 CDN。默认将 org.webjars 映射到 jsdelivr。webJarModuleIds要使用的 webjar 模块 id 序列。默认 ivy 本地仓库与缓存的变化由于 Play 改用 Activator 作为启动器现在它使用默认的 ivy 缓存和本地仓库。这意味着任何你之前发布到 Play ivy 缓存、且仍然依赖的内容都需要重新发布到主目录下.ivy2目录中的本地 ivy 仓库。Results 结果类型重构收尾Play 2.2 中已弃用一批结果类型并引入部分新类型以帮助迁移Play 2.3 完成了这次重构。Scala 结果类型以下来自 Play 2.1 的弃用类型与辅助方法已被移除play.api.mvc.PlainResultplay.api.mvc.ChunkedResultplay.api.mvc.AsyncResultplay.api.mvc.Async如果你的代码仍在使用它们请参考 Play 2.2 迁移指南 了解如何迁移到新的结果结构。按 2.2 时的规划2.3 将play.api.mvc.SimpleResult改名为play.api.mvc.Result取代原有的Resulttrait。为方便迁移引入了类型别名因此你的 Play 2.2 代码在 2.3 下通常源码兼容不过这个别名最终会被移除官方已将其标记为弃用推荐直接切换到Result。在当前仓库的 Results.scala 中可以看到这一设计的最终形态Result是一个case class由ResponseHeader状态码 HTTP 头与HttpEntity响应体组成并附带newSession、newFlash、newCookies、attrs等可选字段同时提供withHeaders、withCookies等一套不可变组合方法——这正是简单结果SimpleResult理念的现代实现。Java 结果类型以下来自 Play 2.1 的弃用类型与辅助方法已被移除play.mvc.Results.asyncplay.mvc.Results.AsyncResult同样的2.3 将play.mvc.SimpleResult改名为play.mvc.Result。这对大多数 Java 代码是透明的受影响最明显的地方是Global.java的错误回调和自定义 action 中。模板引擎独立为 TwirlPlay 2.3 将模板引擎拆分为独立的 Twirl 项目随之而来的是包名与 sbt 设置的变化。内容类型迁移到 twirl 包模板内容类型移到了 twirl 包。如果代码引用了play.mvc.Content类型需更新为play.twirl.api.Content。例如 Play Java 项目中的以下代码import play.mvc.Content; Content html views.html.index.render(42);会产生编译错误[error] ...: incompatible types [error] found : play.twirl.api.Html [error] required: play.mvc.Content需要改为导入play.twirl.api.Content。sbt 设置变化模板的 sbt 设置改由 sbt-twirl 插件提供。此前向模板添加额外导入的写法templatesImport com.abc.backend._现在是TwirlKeys.templateImports org.abc.backend._此前指定自定义模板格式的写法templatesTypes (html - my.HtmlFormat.instance)现在是TwirlKeys.templateFormats (html - my.HtmlFormat.instance)对于使用完整 Scala 语法描述的 sbt 构建可以通过以下方式导入TwirlKeysimport play.twirl.sbt.Import._Html.empty 改为 HtmlFormat.empty如果之前使用Html.empty即play.api.templates.Html.empty现在必须改用play.twirl.api.HtmlFormat.empty。Play WS 变为可选库WS 客户端在 2.3 中成为可选库。如果项目中使用 WS需要显式添加库依赖Java 项目还需要更新包名。Java 项目在build.sbt中添加库依赖libraryDependencies javaWs将源文件中的导入更新为新包import play.libs.ws.*;从当前仓库 transport/client/play-ws 模块的源码看Java WS API 如今正是位于play.libs.ws包下这与迁移后的目标形态一致。Scala 项目在build.sbt中添加库依赖libraryDependencies ws此外使用 WS 客户端现在要求作用域内存在 Play 应用通常通过以下导入实现import play.api.Play.currentWS API 本身也略有变化WS.client现在返回WSClient实例而不再是底层的AsyncHttpClient对象。如需底层客户端可通过WS.client.underlying获取。Anorm 的类型安全强化Play 2.3 的 Anorm 包含多项变更核心方向是提升类型安全。为了让查询参数被正确转换参数的类型必须是可见的。现在直接使用Any作为参数值无论是显式声明还是由于类型擦除导致都会产生编译错误No implicit view available from Any anorm.ParameterValue。// 错误 val p: Any strAsAny SQL(SELECT * FROM test WHERE id{id}). on(id - p) // 错误 - 没有 Any ParameterValue 的转换 // 正确 val p strAsString SQL(SELECT * FROM test WHERE id{id}).on(id - p) // 错误 val ps Seq(a, b, 3) // 被推断为 Seq[Any] SQL(SELECT * FROM test WHERE (a{a} AND b{b}) OR c{c}). on(a - ps(0), // ps(0) - 没有 Any ParameterValue 的转换 b - ps(1), c - ps(2)) // 正确 val ps Seqanorm.ParameterValue // Seq[ParameterValue] SQL(SELECT * FROM test WHERE (a{a} AND b{b}) OR c{c}). on(a - ps(0), b - ps(1), c - ps(2))如果确实需要传递不经过安全转换的值可以使用anorm.Object(anyVal)设置一个不透明参数。同时参数值的类型擦除问题也已修复类型不再写作ParameterValue[_]而是直接写ParameterValue。参数名称的类型也统一了使用.on(...)时现在只支持String和Symbol作为名称。Pk[A] 弃用Pk[A]类型已被弃用。你仍然可以在列映射中使用它但作为查询参数时必须显式传Id[A]或NotAssigned这是类型安全改进的直接后果// 列映射已弃用但可用 val pk: Pk[Long] SQL(SELECT id FROM test WHERE name{n}). on(n - mine).as(get[Pk[Long]].single) // 错误的参数 val pkParam: Pk[Long] Id(1l) val name1 Name #1 SQLINSERT INTO test(id, name) VALUES($pkParam, $name1).execute() // ... pkParam 作为 Pk 传入查询参数 // 现在作为参数类型是错误的无法编译 // 正确的参数 Id val idParam: Id[Long] Id(2l) // 与 pkParam 相同但保持显式 Id 类型 val name2 Name #2 SQLINSERT INTO test(id, name) VALUES($idParam, $name2).execute() // 正确的参数 NotAssigned val name2 Name #3 SQLINSERT INTO test(id, name) VALUES($NotAssigned, $name2).execute()由于被弃用的Pk[A]与Option[A]类似而 Anorm 在列映射和查询参数中都支持Option[A]因此更推荐用Some[A]替换Id[A]、用None替换NotAssigned// 列映射已弃用但可用 val pk: Option[Long] SQL(SELECT id FROM test WHERE name{n}). on(n - mine).as(get[Option[Long]].single) // 已赋值的主键作为参数 val idParam: Option[Long] Some(2l) val name1 Id SQLINSERT INTO test(id, name) VALUES($idParam, $name1).execute() // 正确的参数 NotAssigned val name2 NotAssigned SQLINSERT INTO test(id, name) VALUES($None, $name2).execute()Bootstrap 字段构造器弃用Play 内置的 Bootstrap 字段构造器field constructor已被弃用将在未来版本中移除。原因有二Bootstrap 在不同版本间变化过于剧烈且频繁Play 内置的任何支持很快就会过时无法与当前 Bootstrap 版本兼容当前 Bootstrap 对 CSS class 的需求无法仅靠 Play 的字段构造器实现还需要自定义的输入模板input template。官方观点是如果这对社区有价值可以由第三方模块针对特定 Bootstrap 版本提供一套独立的 Bootstrap 表单辅助模板从而获得远比 Play 内置支持更好的用户体验。Session 超时配置从整数秒到 Durationsession.maxAge配置项过去是整数单位是秒现在它被定义为 Duration时长可以用1h或30m这样的值来指定。注意陷阱如果省略时间单位默认单位是毫秒。因此配置值3600过去表示 1 小时现在只相当于 3.6 秒你需要更新配置为session.maxAge加上时间单位例如play.http.session.maxAge 1h从当前仓库 Session.scala 的 SessionCookieBaker可以看到这一设计在源码层面的落点Session cookie 的maxAge由config.maxAge一个Option[Duration]经.toSeconds.toInt转换而来即配置层接受时长Duration底层 cookie 仍按秒输出——配置语义与 HTTP cookie 语义在此完成衔接。Java JUnit 测试超类自动启动应用Java 的WithApplication、WithServer和WithBrowserJUnit 测试超类都新增了Before注解的方法。此前你需要显式启动应用Before public void setUp() { start(); }现在不再需要。如果需要提供自定义应用可以覆写provideFakeApplication方法Override protected Application provideFakeApplication() { return Helpers.fakeApplication(Helpers.inMemoryDatabase()); }这一设计延续至今在当前仓库的 WithApplication.java中Before public void startPlay()会调用provideApplication()其默认实现返回Helpers.fakeApplication()并启动应用测试结束后的After方法负责停止应用测试期间可通过受保护的app字段访问运行中的应用。WithServer与WithBrowser在此基础上扩展了服务器与浏览器支持WithBrowser.java 直接继承WithServer。Session 与 Flash 隐式参数改名Scala 的 Controller 提供隐式的Session、Flash和Lang参数它们接收隐式RequestHeader。这些参数是为了方便例如模板可以接收隐式参数在 Controller 中会被自动提供。为了避免这些参数名与应用局部同名变量发生遮蔽冲突它们的名字被改了session改为request2Sessionflash改为flash2Sessionlang改为lang2Session任何显式调用这些旧名字的代码都会因此编译失败。在当前仓库的 Controller.scala中可以看到这一约定的实现trait RequestImplicits定义了implicit def request2session(implicit request: RequestHeader): Session request.session与implicit def request2flash(implicit request: RequestHeader): Flash request.flash。官方不推荐显式调用这些隐式方法session、flash和lang都直接暴露在RequestHeader上直接使用RequestHeader的属性会让代码读起来更清楚来源。因此如果代码使用了旧方法建议改为直接访问RequestHeader上对应的属性。迁移检查清单综合全文从 Play 2.2 升级到 2.3 时建议按以下顺序逐项核对命令行工具play→activator并了解activator ui与 mini 发行版构建定义project/build.properties升级到 sbt 0.13.5plugins.sbt中更新 Play 插件到2.3.XXXplayJavaSettings/playScalaSettings→enablePlugins(PlayJava)/enablePlugins(PlayScala)显式声明scalaVersion通过PlayKeys引用 Play 提供的 key资产管线按需引入 sbt-web 系列插件CoffeeScript、LESS、JSHint、rjs、digest、gzip 等用unmanagedResourceDirectories in Assets替代playAssetsDirectories通过 WebJars / npm 管理前端依赖配置pipelineStagesivy 仓库将依赖重新发布到主目录.ivy2的本地 ivy 仓库结果类型移除AsyncResult/Async/Results.async等从SimpleResult切换到Result模板play.mvc.Content→play.twirl.api.ContenttemplatesImport/templatesTypes→TwirlKeys.templateImports/TwirlKeys.templateFormatsHtml.empty→HtmlFormat.emptyWSJava 项目添加javaWs依赖并改用play.libs.ws包Scala 项目添加ws依赖WS.client返回WSClientAnorm消除Any参数必要时用anorm.ObjectPk[A]迁移到Option[A]参数名仅用String/Symbol配置删除对内置 Bootstrap 字段构造器的依赖为session.maxAge补上时间单位测试利用WithApplication/WithServer/WithBrowser自带的Before需要自定义时覆写provideFakeApplication隐式参数将显式调用的session/flash/lang改为RequestHeader上的属性。从 sbt 构建机制、资产管线到运行期 APIPlay 2.3 的每一项变化都可以在当前仓库源码中逐一印证构建与插件机制对应 project 目录下的构建定义结果类型对应 Results.scalaSession 配置对应 Session.scala隐式参数对应 Controller.scalaWS 客户端对应 transport/client/play-ws测试超类对应 testkit/play-test。把迁移过程视为一次理解新架构的机会升级就能顺利完成。赞分享后端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),仅供参考
RELATED

相关推荐

基于WEB的个人知识管理系统架构拆解:Nginx+MongoDB部署实战

基于WEB的个人知识管理系统架构拆解:Nginx+MongoDB部署实战

简介:基于WEB的个人知识管理系统.zip 是一份面向毕业设计学生及Web开发初学者的完整项目源码包,围绕知识采集、分类、存储、检索与共享等核心模块展开,适合用于课程设计、毕设参考或二次开发练习。压缩包共505个文件,大小21.42MB&…

📅 2026/9/23 19:23:20
Tyk OAS 包深度指南:OAS 多版本 Schema 校验、x-tyk-api-gateway 扩展注入与新增版本接入实战

Tyk OAS 包深度指南:OAS 多版本 Schema 校验、x-tyk-api-gateway 扩展注入与新增版本接入实战

API网关后端云原生 【免费下载链接】tyk Open Source API and AI Gateway supporting REST, GraphQL, TCP, gRPC and MCP (Model Context Protocol) 项目地址: https://gitcode.com/gh_mirrors/ty/tyk 点击查看 免费下载 导读 本文以 Tyk 开源 API 网关仓库中的 a…

📅 2026/9/23 19:23:20
GTA5模组整合包安装教程:200+模组兼容性解决方案与避坑指南

GTA5模组整合包安装教程:200+模组兼容性解决方案与避坑指南

1. 这套200模组整合包到底解决了什么问题1.1 从“装一个崩一个”到“一次装完直接玩”玩GTA5的模组,最让人头疼的从来不是找不到模组,而是模组之间的冲突。我自己从2015年开始折腾GTA5的模组,最开始那几年,每次装模组都像在拆炸弹…

📅 2026/9/23 19:23:20
MORE NEWS

更多资讯

📰

Kornia 迁移指南:LocalFeatureMatcher 不再匹配零值 LAF 填充槽,mask0/mask1 正式生效

计算机视觉人工智能深度学习图像处理 【免费下载链接】kornia 🐍 Geometric Computer Vision Library for Spatial AI 项目地址: https://gitcode.com/gh_mirrors/ko/kornia 点击查看 免费下载 本篇迁移指南聚焦 Kornia 特征匹配管线中 LocalFeatureMat…

📰

3个避坑指南:扫描全能王官网技术原理从入门到精通

3个避坑指南:扫描全能王官网技术原理从入门到精通 面对满屏红色的 StackTrace,你是不是脑子嗡的一声,完全不知道从哪行代码看起?这种报错一堆看不懂的感觉,是无数开发者从新手走向老手的必经关卡。很多初学者在接触类似扫描全能王官网这样的…

📰

BCD码原理与工业实战:嵌入式系统中的确定性数字表达

1. 为什么今天还要学BCD码——一个被低估的“数字翻译官”很多人第一次听说BCD码,是在单片机实验课上看到数码管突然亮起一串“0100 0011 0101”,老师说:“这是435的BCD表示。”台下一片茫然:明明二进制就能表示一切,为…

📰

伏安特性与电源外特性测量:从内接外接到数据处理全解析

做过这个实验的同学应该都有同感:电路元件伏安特性的测绘及电源外特性的测量,看起来就是把电压表电流表接上去读数据,但真正动手之后才发现,光是一个“电流表内接还是外接”就能让你数据偏到怀疑人生。这篇内容我会把整个实验从原…

📰

集装箱类型与尺寸全解析:外贸装柜选型避坑指南

做外贸第一年,我最怕客户突然问一句“这个柜子能装多少”。不是不会算,而是很多人把集装箱想得太简单了——铁皮箱子嘛,长宽高一乘不就是体积?实际跑几次装柜现场你就知道,集装箱的类型、尺寸和内径数据里全是门道。选…

📰

手机连打印机保姆级教程:3步搞定API变更痛点

手机连打印机保姆级教程:3步搞定API变更痛点 版本升级后 API 全变了?别慌,这份保姆级教程带你避坑。 很多人卡在蓝牙协议和权限配置上,浪费半天时间。 今天直接上干货,对比主流方案,代码全给你。 方案定位:谁在统治手机打印领域…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬