§使用子项目
一个复杂的项目不一定是单个 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
§将您的 Web 应用程序拆分为多个部分
由于 Play 应用程序只是一个具有默认配置的标准 sbt 项目,因此它可以依赖于另一个 Play 应用程序。您可以通过在相应的 `build.sbt` 文件中添加 `PlayJava` 或 `PlayScala` 插件(取决于您的项目是 Java 项目还是 Scala 项目)来使任何子模块成为 Play 应用程序。
注意:为了避免命名冲突,请确保您的控制器(包括子项目中的 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("...")
§通过浏览器
http://localhost:9000/index
触发
controllers.HomeController.index
以及
http://localhost:9000/admin/index
触发
controllers.admin.HomeController.index
下一步:聚合反向路由器
发现此文档中的错误?此页面的源代码可以在此处找到。在阅读文档指南后,请随时贡献拉取请求。有疑问或建议要分享?请访问我们的社区论坛,与社区开始对话。