Grails一直都是構建於Spring、Groovy、Hibernate和其他巨人肩膀之上的高階框架。到了Grails 3,Grails已經基於Spring Boot,帶來了令人愉悅的開發體驗。Grails開發者和Spring Boot開發者都能駕輕就熟。
要使用Grails 3,首先要進行安裝。在Mac OS X和大部分Unix系統上,最簡單的安裝方法是在命令行裡使用SDKMAN:
$ sdk install grails
如果你用的是Windows,或者無法使用SDKMAN,就需要下載二進制發佈包。解壓後要將bin目錄添加到系統路徑裡去。
無論用哪種安裝方式,你都可以在命令行中查看Grails的版本,驗證安裝是否成功:
$ grails -version
如果安裝成功,現在就可以創建Grails項目了。
6.3.1 創建新的Grails項目
在Grails項目中,你會使用grails
命令行工具執行很多任務,包括創建項目。要創建閱讀列表項目,可以這樣使用grails
命令:
$ grails create-app readinglist
正如這個命令的名字所示,create-app
創建了新的應用程序項目。這個例子裡的項目名為readinglist。
等grails
工具創建完應用程序,cd
到了readinglist
目錄裡,看看所創建的內容吧。圖6-2應該就是你看到的項目結構的概覽。
圖 6-2 Grails 3項目的目錄結構
在這個項目目錄結構裡,你應該認出了一些熟悉的東西。這裡有一個Gradle的構建說明文件和配置(build.gradle和gradle.properties)。src目錄裡還有一個標準的Gradle項目結構,但是grails-app應該是裡面最有趣的目錄。如果用過老版本的Grails,你就會知道這個目錄的作用。這裡面放的是你寫的控制器、領域類和其他構成Grails項目的代碼。
如果再深挖一下,打開build.gradle文件,會發現一些更熟悉的東西。首先,構建說明文件裡使用了Spring Boot的Gradle插件:
apply plugin: \"spring-boot\"
這意味著你能像使用其他Spring Boot應用程序那樣構建並運行這個Grails應用程序。
你還應該注意到,依賴裡有不少有用的Spring Boot庫:
dependencies {
compile \'org.springframework.boot:spring-boot-starter-logging\'
compile(\"org.springframework.boot:spring-boot-starter-actuator\")
compile \"org.springframework.boot:spring-boot-autoconfigure\"
compile \"org.springframework.boot:spring-boot-starter-tomcat\"
...
}
這些庫為Grails應用程序提供了Spring Boot的自動配置、日誌,還有Actuator及嵌入式Tomcat。把應用當作可執行JAR運行時,這個Tomcat可以提供服務。
實際上,這是一個Spring Boot項目,同時也是Grails項目,因為Grails 3就是構建在Spring Boot的基礎上的。
運行應用程序
運行Grails應用程序最直接的方式是在命令行裡使用grails
工具的run-app
命令:
$ grails run-app
就算一行代碼都還沒寫,我們也能運行應用程序,在瀏覽器裡進行訪問。一旦應用程序啟動,就可以在瀏覽器裡訪問http://localhost:8080。你應該能看到類似圖6-3的頁面。
圖 6-3 全新的Grails應用程序
在Grails裡運行應用程序要使用run-app
命令,這種方式已經用了很多年,上個版本的Grails也是這樣。Grails 3項目的Gradle說明裡使用了Spring Boot的Gradle插件,你還可以用各種運行Spring Boot項目的方式來運行這個應用程序。此處通過Gradle引入了bootRun
任務:
$ gradle bootRun
你還可以構建項目,運行生成的可執行JAR文件:
$ gradle build
...
$ java -jar build/lib/readingList-0.1.jar
當然,構建產生的WAR文件可以部署到你喜歡的各種Servlet 3.0容器裡。
在開發早期就能運行應用程序,這一點十分方便,能幫你確認應用程序已正確初始化。但是這時應用程序還沒做什麼有意思的事情,在初始化後的項目上做什麼完全取決於我們。接下來,開始定義領域模型吧。
6.3.2 定義領域模型
閱讀列表應用程序裡的核心領域模型是Book
類。雖然我們可以手工創建Book.groovy文件,但通常還是用grails
工具來創建領域模型類比較好。因為它知道該把文件放到哪裡,並且能在同一時間生成各種相關內容。
要創建Book
類,我們會使用grails
工具的create-domain-class
命令:
$ grails create-domain-class Book
這條命令會生成兩個源文件:一個Book.groovy文件和一個BookSpec.groovy文件。後者是一個Spock說明,用來測試Book
類。一開始這個文件是空的,你可以填入各種測試內容來驗證Book
的各種功能。
Book.groovy文件裡定義了Book
類,你可以在grails-app/domain/readingList裡找到這個文件。它一開始基本沒什麼內容:
package readinglist
class Book {
static constraints = {
}
}
我們需要添加一些字段來定義一本書,比如書名、作者和ISBN。在添加了這些字段後,Book.groovy看起來是這樣的:
package readinglist
class Book {
static constraints = {
}
String reader
String isbn
String title
String author
String description
}
靜態的constraints
變量裡可以定義各種附加在Book
實例上的驗證約束。本章中,我們主要關注閱讀列表應用程序的構建,看看如何基於Spring Boot構建應用程序,不會太關注驗證的問題。因此,這裡的constraints
內容為空。當然,如果有需要的話,你可以隨意添加約束。可以參考一下Grails in Action\'Second Edition,作者是Glen Smith和Peter Ledbrook。1
1這本書雖然主講Grails 2,但你在Grails 2里瞭解到的大部分內容都適用於Grails 3。
為了使用Grails,我們要保持閱讀列表應用程序的簡潔,要和第2章的程序一致。因此,接下來我們要創建Reader
領域模型,還有控制器。
6.3.3 開發Grails控制器
有了領域模型,通過grails
工具創建出控制器就很容易了。關於控制器,有幾個命令可供選擇。
create-controller
:創建空控制器,讓開發者來編寫控制器的功能。generate-controller
:生成一個控制器,其中包含特定領域模型類的基本CRUD操作。generate-all
:生成針對特定領域模型類的基本CRUD控制器,及其視圖。
腳手架控制器很好用,也是Grails中比較知名的特性,但我們仍然會保持簡潔,寫一個僅包含必要功能的控制器,能匹配第2章裡的應用程序功能就好。因此,我們用create-controller
命令來創建原始的控制器,然後填入所需的方法。
$ grails create-controller ReadingList
這個命令會在grails-app/controllers/readingList裡創建一個名為ReadingListController
的控制器:
package readinglist
class ReadingListController {
def index { }
}
一行代碼都不用改,這個控制器就能運行了,雖然它幹不成什麼事。此時,它能處理發往/readingList的請求,將請求轉給grails-app/views/readingList/index.gsp裡定義的視圖(現在還沒有,我們稍後會創建的)。
我們需要控制器來顯示圖書列表,還有添加新書的表單。我們還需要提交表單,將新書保存到數據庫裡的功能。下面的代碼就是我們所需要的ReadingListController
。
代碼清單6-6 改寫
ReadingListController
package readinglist
import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional
class ReadingListController {
def index {
respond Book.list(params), model:[book: new Book] ←---獲取圖書填充到模型裡
}
@Transactional
def save(Book book) {
book.reader = \'Craig\'
book.save flush:true ←---保存圖書
redirect(action: \"index\")
}
}
雖然相比等效的Java控制器,代碼長度大幅縮短,但這個版本的ReadingListController
功能已經基本完整。它可以處理發往/readingList的GET
請求,獲取並展示圖書列表。在表單提交後,它還會處理POST
請求,保存圖書,隨後重定向回index動作(由index
方法來處理)。
太不可思議了,我們已經基本完成了Grails版本的閱讀列表應用程序。剩下的就是創建一個視圖,顯示圖書列表和表單。
6.3.4 創建視圖
Grails應用程序通常都用GSP模板來做視圖。你已經看到過如何在Spring Boot應用程序裡使用GSP了,因此,此處的模板並不會和6.2節裡的模板有太多不同。
我們要做的是,利用Grails提供的佈局設施,將公共的設計風格運用到整個應用程序裡。如代碼清單6-7所示,這就是個很簡單的修改。
代碼清單6-7 一個適用於Grails的GSP模板,包含佈局
<!DOCTYPE html>
<html>
<head>
<meta name=\"layout\" content=\"main\"/> ←---使用了main佈局
<title>Reading List</title>
<link rel=\"stylesheet\"
href=\"/assets/main.css?compile=false\" />
<link rel=\"stylesheet\"
href=\"/assets/mobile.css?compile=false\" />
<link rel=\"stylesheet\"
href=\"/assets/application.css?compile=false\" />
</head>
<body>
<h2>Your Reading List</h2>
<g:if test=\"${bookList && !bookList.isEmpty}\"> ←---列出圖書
<g:each in=\"${bookList}\" var=\"book\">
<dl>
<dt>
${book.title}</span> by ${book.author}
(ISBN: ${book.isbn}\")
</dt>
<dd>
<g:if test=\"${book.description}\">
${book.description}
</g:if>
<g:else>
No description available
</g:else>
</dd>
</dl>
</g:each>
</g:if>
<g:else>
<p>You have no books in your book list</p>
</g:else>
<hr/>
<h3>Add a book</h3>
<g:form action=\"save\"> ←---圖書表單
<fieldset>
<label for=\"title\">Title:</label>
<g:field type=\"text\" name=\"title\" /><br/>
<label for=\"author\">Author:</label>
<g:field type=\"text\" name=\"author\"
/><br/>
<label for=\"isbn\">ISBN:</label>
<g:field type=\"text\" name=\"isbn\" /><br/>
<label for=\"description\">Description:</label><br/>
<g:textArea name=\"description\"
rows=\"5\" cols=\"80\"/>
</fieldset>
<fieldset>
<g:submitButton name=\"create\"
/>
</fieldset>
</g:form>
</body>
</html>
在<head>
元素裡移除了引用樣式表的<link>
標籤。這裡放了一個<meta>
標籤,引入了Grails應用程序的main
佈局。這樣一來,應用程序就能用上Grails的外觀了,運行效果如圖6-4所示。
圖 6-4 應用通用Grails風格的閱讀列表應用程序
雖然Grails風格比之前用的簡單的樣式表更吸引眼球。但很顯然的是,要讓閱讀列表應用程序更好看,還有一些工作要做。首先要讓應用程序和Grails不那麼像,和我們的想像更接近一點。修改應用程序的樣式表在本書的討論範圍之外,但如果你對樣式微調感興趣,可以在grails-app/ assets/stylesheets目錄裡找到樣式表文件。