Play! Framework 系列(二):play 的项目结构
在?Play! Framework 系列(一)中我們初步了解了一下 Play! 的各種特性以及優勢,那么從現在開始我們將正式接觸 Play!。本文將介紹一下 Play! 的整體結構,然后通過一個非常簡單的例子來闡述各個結構之間的關系以及如何利用 Play! 約定的結構去合理地組織我們的業務邏輯。
結構概覽
上圖為基于 Play! 而創建的一個簡單的 Web 應用,在上一篇文章中我們說過 Play! 是「ROR」風格的框架,通過上圖我們也可以看到 Play! 是典型的 MVC 架構框架,另外 Play! 也采用「約定優于配置」,我們只需要按照其約定的結構去組織我們的代碼就可以很輕松地實現一個 Web 應用,那么接下來我們就去了解一下 Play! 中各個結構的特點以及功能吧。
業務描述
我們將通過實現一個小應用的方式去了解 Play! 的基本結構,這樣會更加清晰一些。需求描述:
- 實現一個簡單的公司員工信息列表。
可以看到,我們將要實現的 Web 應用非常簡單,接下來我們就通過這個小小的需求去把玩一下 Play! 吧。
app
1 2 3 4 | app └ controllers └ models └ views |
目錄 app 排在結構圖中的最上面,因為是按照首字母排列的,所以它理應在最前面。當然,它在我們整個 Play 應用中也是非常重要的,幾乎我們所有的業務代碼都包含在該目錄下面,既然它如此重要,排在最前面也無可厚非。在 app 下三個子目錄,分別是:controllers、models 以及 views。
我們也可以在 app 目錄下增加一些目錄,比如,我們需要利用 Play! 的 Filter (后面會介紹)來實現一些需求,那么我們可以在該目錄下新增一個 filters 目錄,專門用來管理 Filter 的業務邏輯。例如:
1 2 3 4 5 | app └ controllers └ models └ views └ filters |
接下來我們將詳細介紹該目錄下的三個核心結構:controllers、models 以及 views。
models
在 MVC 結構的 Web 應用中,M 對應的就是 Model,在 models 下,我們實現數據訪問的一些邏輯,一般來說,數據庫中的一個表就對應一個 model 類。例如:
我們將要顯示「員工」列表,這里我們需要數據庫中的「員工表」,那么在 models 下,我們創建一個表示員工信息的 model:
1 2 3 4 5 6 | caseclassEmployee ( id: Long, name: String, sex: String, position: String ) |
一般情況下,我們也需要在 models 下實現操作數據庫的邏輯,但是當業務比較復雜的時候,整個文件看上去會特別凌亂,并且后期也不好維護,所以這里我們引入 services,我們將在 services 下實現所有與數據庫打交道的邏輯,而 models 下,我們只需要它定義相應的 model 類就可以了。
1 2 3 4 5 | app └ controllers └ models └ views └ services |
services
我們將在 services 下新建一個 EmployeeService 去實現員工信息的查詢操作:
注:本文不涉及數據庫,所以在這里我們把數據都寫死,數據庫連接后面的文章會詳細講解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | classEmployeeService { 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) } |
views
View 對應的就是 MVC 結構中的 V,在該結構下,我們實現程序中的視圖,也就是利用 Play! 的模板去實現 html 頁面,在 view 中,我們一般只做數據的渲染,很少實現復雜的邏輯。為了呈現員工列表,我們在 views 下創建一個名為 employeeList.scala.html 的文件,在該文件下,我們主要實現數據的渲染,這里只寫一些主要的代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @(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> |
controllers
前面我們創建好了 model、servic 以及 view,那如何將 model、service 中的數據渲染到 view 中去呢?這個時候就需要 controller 了,Controller 對應于 MVC 中的C,在 controllers 下面,我們需要實現一些列的 action,通過這些 action 來將整個 Web 程序的數據聯系在一起。為了將前面創建的 model、service 以及 view 聯系起來,我們在 controllers 下創建一個 EmployeeController:
1 2 3 4 5 6 7 8 9 10 11 12 | classEmployeeController@Inject() ( cc: ControllerComponents ) extends AbstractController(cc) { val employeeSerivce = newEmployeeSerivce 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:
1 2 3 4 | // object Action 的 apply 方法 finaldef apply(block: ?Result): Action[AnyContent] |
conf
1 2 3 4 | conf └ application.conf └ routes |
在 conf 下面,我們主要放置整個項目的配置文件和路由文件。
application.conf
該文件將配置 Play! 應用的一系列信息,比如 secret key,數據庫信息等,由于我們的應用比較簡單,所以這里不需要配置該項,在后面的文章中,我們將專門介紹如何管理 application.conf。
routes
前面我們實現了 model、service、controller 以及 view,那我們如何通過瀏覽器去訪問我們的應用呢,這里就需要使用「路由」了,應用程序的所有路由都將在 routes 中實現,這些路由就是應用程序的入口。例如:
要想訪問我們之前實現的「員工列表」,我們就需要在 routes 中指定相應的路由:
1 2 | GET /employee/employee-list controllers.EmployeeController.employeeList |
指定好路由之后,當我們在瀏覽器中輸入?http://localhost:9000/employee/employee-list?的時候,就能訪問到「員工列表」頁面了。
關于 routes,我們在 route 文件中只是寫了這么一段去指定,當編譯完成之后,我們在?target/scala-2.12/routes/main/router/?下可以看到一個名為 Route.scala 的文件,在文件的末尾可以看到:
1 2 3 4 5 6 7 8 | 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。
build.sbt
該文件用來定義我們項目的一些基本信息以及項目所需要的一些依賴的信息,比如項目的名稱、所屬組織、版本信息、scala 的版本以及一些依賴的定義等等,在我們的應用中,build.sbt 可以這樣定義:
1 2 3 4 5 6 7 8 9 10 11 12 | name := "HelloWorld" organization := "com.shawdubie" version := "1.0-SNAPSHOT" lazyval 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
1 2 3 4 | project └ build.properties └ plugins.sbt |
該目錄下主要放置 sbt 構建之后的文件,在構建之前,該目錄下一般就只有上面所列的兩個文件。
build.properties
這里定義了該項目所依賴的 sbt 的版本信息,例如該項目中 sbt 的版本就可以這樣聲明:
1 | sbt.version=0.13.15 |
plugins.sbt
在該文件下我們聲明該項目所依賴的一些插件,比如我們使用了 play sbt 插件:
1 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3") |
結語
本文通過一個例子讓我們大致了解了 Play! 的基本結構,文中有一些一筆帶過的內容我們將在后面的文章中詳細介紹,這里只需要知道就可以了。本文的例子請戳?源碼鏈接
?
本文轉載自:https://scala.cool/2017/09/play2/
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Play! Framework 系列(二):play 的项目结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Play! Framework 系列(一
- 下一篇: Play! Framework 系列(三