本文由 Shaw 發表在 ScalaCool 團隊博客。html
在 Play! Framework 系列(一)中咱們初步瞭解了一下 Play! 的各類特性以及優點,那麼從如今開始咱們將正式接觸 Play!。本文將介紹一下 Play! 的總體結構,而後經過一個很是簡單的例子來闡述各個結構之間的關係以及如何利用 Play! 約定的結構去合理地組織咱們的業務邏輯。git
上圖爲基於 Play! 而建立的一個簡單的 Web 應用,在上一篇文章中咱們說過 Play! 是「ROR」風格的框架,經過上圖咱們也能夠看到 Play! 是典型的 MVC 架構框架,另外 Play! 也採用 「約定因爲配置」,咱們只須要按照其約定的結構去組織咱們的代碼就能夠很輕鬆地實現一個 Web 應用,那麼接下來咱們就去了解一下 Play! 中各個結構的特色以及功能吧。程序員
咱們將經過實現一個小應用的方式去了解 Play! 的基本結構,這樣會更加清晰一些。需求描述:github
能夠看到,咱們將要實現的 Web 應用很是簡單,接下來咱們就經過這個小小的需求去把玩一下 Play! 吧。數據庫
app
└ controllers
└ models
└ views複製代碼
目錄 app 排在結構圖中的最上面,由於是按照首字母排列的,因此它理應在最前面。固然,它在咱們整個 Play 應用中也是很是重要的,幾乎咱們全部的業務代碼都包含在該目錄下面,既然它如此重要,排在最前面也無可厚非。在 app 下三個子目錄,分別是:controllers、models 以及 views。api
咱們也能夠在 app 目錄下增長一些目錄,好比,咱們須要利用 Play! 的 Filter (後面會介紹)來實現一些需求,那麼咱們能夠在該目錄下新增一個 filters 目錄,專門用來管理 Filter 的業務邏輯。例如:瀏覽器
app
└ controllers
└ models
└ views
└ filters複製代碼
接下來咱們將詳細介紹該目錄下的三個核心結構:controllers、models 以及 views。bash
在 MVC 結構的 Web 應用中,M 對應的就是 Model,在 models 下,咱們實現數據訪問的一些邏輯,通常來講,數據庫中的一個表就對應一個 model 類。例如:架構
咱們將要顯示「員工」列表,這裏咱們須要數據庫中的「員工表」,那麼在 models 下,咱們建立一個表示員工信息的 model:mvc
case class Employee ( id: Long, name: String, sex: String, position: String )複製代碼
通常狀況下,咱們也須要在 models 下實現操做數據庫的邏輯,可是當業務比較複雜的時候,整個文件看上去會特別凌亂,而且後期也很差維護,因此這裏咱們引入 services,咱們將在 services 下實現全部與數據庫打交道的邏輯,而 models 下,咱們只須要它定義相應的 model 類就能夠了。
app
└ controllers
└ models
└ views
└ services複製代碼
咱們將在 services 下新建一個 EmployeeService 去實現員工信息的查詢操做:
注:本文不涉及數據庫,因此在這裏咱們把數據都寫死,數據庫鏈接後面的文章會詳細講解。
class EmployeeService {
val jilen = Employee(
id = 1,
name = "Jilen",
sex = "男",
position = "全乾工程師"
)
val yison = Employee(
id = 2,
name = "Yison",
sex = "女",
position = "程序員鼓勵師"
)
def getEmployees: Seq[Employee] = Seq(jilen, yison)
}複製代碼
View 對應的就是 MVC 結構中的 V,在該結構下,咱們實現程序中的視圖,也就是利用 Play! 的模板去實現 html 頁面,在 view 中,咱們通常只作數據的渲染,不多實現複雜的邏輯。爲了呈現員工列表,咱們在 views 下建立一個名爲 employeeList.scala.html 的文件,在該文件下,咱們主要實現數據的渲染,這裏只寫一些主要的代碼:
@(employees: Seq[Employee])
<table class="employee-list">
<tr>
<th>編號</th>
<th>姓名</th>
<th>性別</th>
<th>職位</th>
</tr>
@for(e <- employees){
<tr>
<td>@e.id</td>
<td>@e.name</td>
<td>@e.sex</td>
<td>@e.position</td>
</tr>
}
</table>複製代碼
前面咱們建立好了 model、servic 以及 view,那如何將 model、service 中的數據渲染到 view 中去呢?這個時候就須要 controller 了,Controller 對應於 MVC 中的 的 C,在 controllers 下面,咱們須要實現一些列的 action,經過這些 action 來將整個 Web 程序的數據聯繫在一塊兒。爲了將前面建立的 model、service 以及 view 聯繫起來,咱們在 controllers 下建立一個 EmployeeController:
class EmployeeController @Inject() ( cc: ControllerComponents ) extends AbstractController(cc) {
val employeeSerivce = new EmployeeSerivce
def employeeList = Action { implicit request: Request[AnyContent] =>
val employees = employeeSerivce.getEmployees()
Ok(views.html.employeeList(employees))
}
}複製代碼
這裏咱們簡單介紹一下 Play 中的 Action,Play 中的 「Action」 其實是一個「特質(trait)」,咱們上面的代碼實現了一個 「Action」, 這裏其實是使用了 object Action,而後 「object Action」 中的 「apply」 方法會返回一個 Action:
// object Action 的 apply 方法
final def apply(block: ⇒ Result): Action[AnyContent]複製代碼
conf
└ application.conf
└ routes複製代碼
在 conf 下面,咱們主要放置整個項目的配置文件和路由文件。
該文件將配置 Play! 應用的一系列信息,好比 secret key,數據庫信息等,因爲咱們的應用比較簡單,因此這裏不須要配置該項,在後面的文章中,咱們將專門介紹如何管理 application.conf。
前面咱們實現了 model、service、controller 以及 view,那咱們如何經過瀏覽器去訪問咱們的應用呢,這裏就須要使用「路由」了,應用程序的全部路由都將在 routes 中實現,這些路由就是應用程序的入口。例如:
要想訪問咱們以前實現的「員工列表」,咱們就須要在 routes 中指定相應的路由:
GET /employee/employee-list controllers.EmployeeController.employeeList複製代碼
指定好路由以後,當咱們在瀏覽器中輸入 http://localhost:9000/employee/employee-list
的時候,就能訪問到「員工列表」頁面了。
關於 routes,咱們在 route 文件中只是寫了這麼一段去指定,當編譯完成以後,咱們在 target/scala-2.12/routes/main/router/
下能夠看到一個名爲 Route.scala 的文件,在文件的末尾能夠看到:
def routes: PartialFunction[RequestHeader, Handler] = {
case controllers_EmployeeController_employeeList0_route(params) =>
call {
controllers_EmployeeController_employeeList0_invoker.call(EmployeeController_0.employeeList)
}
}複製代碼
可見其實 routes 在 play! 中的實現是一個方法,它是一個「偏函數」當某個請求被匹配到了就調用相應的方法,若是沒有匹配到則報錯,因此咱們也能夠本身實現某個路由,而不用 play! 的這種方式,固然用 play! 約定好會更加清晰和簡單。
在介紹完 routes 以後,咱們有必要知道一下當咱們在瀏覽器中輸入某個連接的時候,play! 的各個模塊之間是如何調用的,以下圖:
當咱們訪問某個連接的時候,該連接就是對應的一個路由,該路由會去匹配某個 Controller 中的 Action,接下來 Action 要去調用所依賴的 Service 中的方法,這些方法將數據獲取到傳遞給 Action,而後 Action 將這些數據送給 View,View 就會將咱們所須要的頁面渲染出來了。這個流程如圖中的實線所示,同時 Controller 也會依賴 Model,有時候 View 也會去依賴 Model 以及 Service。
該文件用來定義咱們項目的一些基本信息以及項目所須要的一些依賴的信息,好比項目的名稱、所屬組織、版本信息、scala 的版本以及一些依賴的定義等等,在咱們的應用中,build.sbt 能夠這樣定義:
name := "HelloWorld"
organization := "com.shawdubie"
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.12.2"
libraryDependencies += guice
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.0" % Test複製代碼
build.sbt 文件在 sbt 啓動的時候就會被讀取,而後 sbt 就會去加載咱們在裏面定義的一些信息,好比咱們聲明的一些依賴。build.sbt 能夠包含許多信息,關於更詳細的咱們後面再討論,這裏只須要知道她。
project
└ build.properties
└ plugins.sbt複製代碼
該目錄下主要放置 sbt 構建以後的文件,在構建以前,該目錄下通常就只有上面所列的兩個文件。
這裏定義了該項目所依賴的 sbt 的版本信息,例如該項目中 sbt 的版本就能夠這樣聲明:
sbt.version=0.13.15複製代碼
在該文件下咱們聲明該項目所依賴的一些插件,好比咱們使用了 play sbt 插件:
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3")複製代碼
本文經過一個例子讓咱們大體瞭解了 Play! 的基本結構,文中有一些一筆帶過的內容咱們將在後面的文章中詳細介紹,這裏只須要知道就能夠了。本文的例子請戳 源碼連接