文件

§使用子專案

複雜的專案不一定要由單一 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 檔案中加入 PlayJavaPlayScala 外掛,來讓任何子模組成為 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

下一頁:彙總反向路由器


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