§使用子專案
複雜的專案不一定要由單一 Play 應用程式組成。您可能想要將大型專案分割成數個較小的應用程式,甚至將一些邏輯萃取到與 Play 應用程式無關的標準 Java 或 Scala 函式庫中。
閱讀 sbt 多專案建置文件 會有所幫助。子專案可以在父專案的建置檔案中完整定義,儘管我們在此將子專案設定值放在它們自己的建置檔案中。
§新增一個簡單的函式庫子專案
你可以讓你的應用程式依賴一個簡單的函式庫專案。只要在你的 build.sbt
檔案中新增另一個 sbt 專案定義即可
name := "my-first-application"
version := "1.0"
lazy val myFirstApplication = (project in file("."))
.enablePlugins(PlayScala)
.aggregate(myLibrary)
.dependsOn(myLibrary)
lazy val myLibrary = project
最後一行的小寫 project
是 Scala 巨集,它會使用指派給它的 val 的名稱來決定專案名稱和資料夾。
myFirstApplication
專案宣告基礎專案。如果你沒有任何子專案,這已經隱含在其中,但是當宣告子專案時,通常需要宣告它,以便你可以確保它會聚合(也就是在基礎專案中執行時,在子專案上執行編譯/測試等工作)並依賴(也就是將子專案新增到主要專案的類別路徑)子專案。
上述範例在應用程式的 myLibrary
資料夾中定義一個子專案。這個子專案是一個標準的 sbt 專案,使用預設配置
myProject
└ build.sbt
└ app
└ conf
└ public
└ myLibrary
└ build.sbt
└ src
└ main
└ java
└ scala
myLibrary
有自己的 build.sbt
檔案,它可以在這裡宣告自己的設定值、依賴項等。
當你的建置中啟用一個子專案時,你可以專注於這個專案並個別編譯、測試或執行它。只要在 Play 主控台提示字元中使用 projects
指令即可顯示所有專案
[my-first-application] $ projects
[info] In file:/Volumes/Data/gbo/myFirstApp/
[info] * my-first-application
[info] my-library
預設專案是其變數名稱在字母順序中排在第一個的專案。你可以透過將其變數名稱設為 aaaMain 來設定你的主要專案。若要變更目前的專案,請使用 project
指令
[my-first-application] $ project my-library
[info] Set current project to my-library
>
當你在開發模式下執行你的 Play 應用程式時,依賴專案會自動重新編譯,而且如果有些東西無法編譯,你會在瀏覽器中看到結果
§共用變數和程式碼分享
如果你想要你的子專案和根專案分享一些共用的設定或程式碼,那麼這些東西可以放在根專案的 project
目錄中的 Scala 檔案中。例如,在 project/Common.scala
中你可能會這樣做
import sbt._
import Keys._
object Common {
val settings: Seq[Setting[_]] = Seq(
organization := "com.example",
version := "1.2.3-SNAPSHOT"
)
val fooDependency = "com.foo" %% "foo" % "2.4"
}
然後在你的每個 build.sbt
檔案中,你可以參考檔案中宣告的任何東西
name := "my-sub-module"
Common.settings
libraryDependencies += Common.fooDependency
需要注意的一件事是,如果你混用 Play 和非 Play 專案,你可能需要明確地分享 Play 組態。例如,你可能想要為每個 Play 專案分享 InjectedRoutesGenerator
和 specs2
object Common {
val playSettings = settings ++ Seq(
routesGenerator := InjectedRoutesGenerator,
libraryDependencies += specs2 % Test,
resolvers += Resolver.ApacheMavenSnapshotsRepo // contains pekko(-http) snapshots
)
}
而在子專案的 build.sbt
檔案中,你會這樣做
Common.playSettings
§將你的網路應用程式分割成好幾個部分
由於 Play 應用程式只是一個具有預設組態的標準 sbt 專案,它可以依賴於另一個 Play 應用程式。你可以透過在相應的 build.sbt
檔案中加入 PlayJava
或 PlayScala
外掛,來讓任何子模組成為 Play 應用程式,這取決於你的專案是 Java 專案還是 Scala 專案。
注意:為了避免命名衝突,請確定你的控制器,包括子專案中的 Assets 控制器,使用與主專案不同的名稱空間。例如,
admin
模組中的控制器應該具有admin.MyController
的完整限定套件名稱。
§分割路由檔案
也可以將路由檔案分割成較小的部分。如果你想要建立一個強健、可重複使用的多模組 Play 應用程式,這是一個非常方便的功能。
§考慮以下建置組態
build.sbt
:
name := "myproject"
lazy val admin = (project in file("modules/admin")).enablePlugins(PlayScala)
lazy val main = (project in file("."))
.enablePlugins(PlayScala).dependsOn(admin).aggregate(admin)
modules/admin/build.sbt
name := "myadmin"
libraryDependencies ++= Seq(
"com.mysql" % "mysql-connector-j" % "8.0.33",
jdbc,
anorm
)
§專案結構
build.sbt
app
└ controllers
└ models
└ views
conf
└ application.conf
└ routes
modules
└ admin
└ build.sbt
└ conf
└ admin.routes
└ app
└ controllers
└ models
└ views
project
└ build.properties
└ plugins.sbt
注意:組態和路由檔案名稱在整個專案結構中必須是唯一的。特別是,只能有一個
application.conf
檔案和一個routes
檔案。若要在子專案中定義額外的路由或組態,請使用子專案特定的名稱。例如,admin
中的路由檔案稱為admin.routes
。若要在開發模式中對子專案使用特定設定集,最好將這些設定放入組建檔案中,例如PlayKeys.devSettings += ("play.http.router", "admin.Routes")
。
conf/routes
:
GET /index controllers.HomeController.index()
-> /admin admin.Routes
GET /assets/*file controllers.Assets.at(path="/public", file)
modules/admin/conf/admin.routes
:
GET /index controllers.admin.HomeController.index()
GET /assets/*file controllers.Assets.at(path="/public/lib/myadmin", file)
注意:資源會從唯一的類別載入器提供,因此資源路徑必須相對於專案類別路徑根目錄。
子專案資源會產生在target/web/public/main/lib/{module-name}
中,因此當使用play.api.Application#resources(uri)
方法(也就是Assets.at
方法所執行的動作)時,可以從/public/lib/{module-name}
存取資源。
§資源和控制器類別都應該定義在 controllers.admin
套件中
- Java
-
package controllers.admin; import controllers.AssetsMetadata; import javax.inject.Inject; import play.api.Environment; import play.api.http.HttpErrorHandler; import play.api.mvc.*; public class Assets extends controllers.Assets { @Inject public Assets(HttpErrorHandler errorHandler, AssetsMetadata meta, Environment env) { super(errorHandler, meta, env); } public Action<AnyContent> at(String path, String file) { boolean aggressiveCaching = true; return super.at(path, file, aggressiveCaching); } }
- Scala
-
import javax.inject._ import play.api.http.HttpErrorHandler import play.api.Environment class Assets @Inject() ( errorHandler: HttpErrorHandler, assetsMetadata: controllers.AssetsMetadata, environment: Environment ) extends controllers.AssetsBuilder(errorHandler, assetsMetadata, environment)
以及控制器
- Java
-
/* * Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com> */ package controllers.admin; import play.mvc.Controller; import play.mvc.Result; public class HomeController extends Controller { public Result index() { return ok("admin"); } }
- Scala
-
package controllers.admin import javax.inject.Inject import play.api.mvc._ class HomeController @Inject() (val controllerComponents: ControllerComponents) extends BaseController { def index: Action[AnyContent] = Action { implicit request => Ok("admin") } }
§admin
中的反向路由
如果是一般控制器呼叫
controllers.admin.routes.HomeController.index
以及 Assets
controllers.admin.routes.Assets.at("...")
§透過瀏覽器
https://127.0.0.1:9000/index
觸發
controllers.HomeController.index
以及
https://127.0.0.1:9000/admin/index
觸發
controllers.admin.HomeController.index
下一頁:彙總反向路由器
在此文件發現錯誤?此頁面的原始碼可在 此處 找到。閱讀 文件指南 後,請隨時貢獻拉取請求。有問題或建議要分享嗎?前往 我們的社群論壇 與社群展開對話。