使用Play框架快速進(jìn)行Java Web應(yīng)用開發(fā)
雖然目前有很多種 Java Web 應(yīng)用開發(fā)框架,但Play 框架是一種新興的框架,可以幫助開發(fā)人員高效的構(gòu)建Web 應(yīng)用。51CTO在《Play!——另一個Java Web應(yīng)用框架》介紹過Play 框架是一個full-stack(全棧的)Java Web應(yīng)用框架,包括一個簡單的無狀態(tài)MVC模型,具有Hibernate的對象持續(xù),一個基于Groovy的模板引擎,以及建立一個現(xiàn)代Web應(yīng)用所需的所有東西。
Play的關(guān)鍵特性:
1、一個非常簡單的開發(fā)周期。此框架自動編譯和重新裝載源文件的任何改變。
2、智能捆綁HTTP參數(shù)到Java方法參數(shù)。
3、基于Apache Mina的快速HTTP服務(wù)器。
4、一個基于Groovy的強大的模板引擎,具有多層繼承,定制用戶標(biāo)簽的能力,等。
5、優(yōu)秀的錯誤報告功能:當(dāng)發(fā)生異常,此框架會直接顯示出錯代碼,甚至是模板代碼。
本文詳細(xì)介紹了 Play 框架的使用,包括模型層、控制層、視圖層和 HTTP 路由等內(nèi)容。通過本文的介紹,開發(fā)人員可以了解到如何用 Play 框架來快速開發(fā) Web 應(yīng)用。
使用 Play 框架可以方便和高效的開發(fā)出 Java Web 應(yīng)用。通過 Play 框架提供的命令行工具,可以快速的創(chuàng)建出一個 Web 應(yīng)用的基本骨架。它的 Java 代碼動態(tài)編譯機制,使得修改代碼之后,不需要重啟服務(wù)器就可以直接看到修改之后的結(jié)果,調(diào)試起來非常方便。它使用 JPA 規(guī)范來完成領(lǐng)域?qū)ο蟮某志没梢院芊奖愕氖褂貌煌年P(guān)系數(shù)據(jù)庫作為后臺存儲。使用 Play 框架可以很容易的構(gòu)建使用 REST 架構(gòu)風(fēng)格的應(yīng)用。它使用 Groovy 作為視圖層模板使用的表達(dá)式語言。模板之間的繼承機制也可以避免代碼的重復(fù)。總的來說,Play 框架非常適合快速 Web 應(yīng)用開發(fā)。
Play 框架采用經(jīng)典的 MVC 架構(gòu),把 Web 應(yīng)用分成模型層、控制層和視圖層三個層次。每個層次對應(yīng)的文件被存放在不同的目錄下面,方便組織和管理。使用 Play 框架的 Web 應(yīng)用具有相同的目錄結(jié)構(gòu),如 圖 1所示。
圖 1. 使用 Play 框架的 Web 應(yīng)用的目錄結(jié)構(gòu)
如 圖 1所示,應(yīng)用自身的文件被放在 app目錄下面,三個子目錄分別存放的是 MVC 模式的三個層次的內(nèi)容。其中 models和 controllers目錄下面是 Java 源文件,而 views目錄下面則是視圖層使用的模板文件。conf目錄下面存放的是應(yīng)用的配置文件、HTTP 路由文件和國際化所需的消息文件。public目錄則是存放 Web 應(yīng)用的靜態(tài)文件,包括 JavaScript、CSS 和圖像文件等。lib目錄存放所需的額外的 Java 庫。test目錄存放的是測試結(jié)果。
開發(fā)環(huán)境
本文中使用的 Play 框架的版本是 1.0.3.1,使用的集成開發(fā)環(huán)境是 Eclipse 3.6, 使用 Dojo 作為 JavaScript 框架。在 Play 框架官方網(wǎng)站(見 參考資料)下載 Play 框架的壓縮包之后,解壓到某個目錄,并把該目錄下面的 bin目錄添加到環(huán)境變量中。接著啟動一個命令窗口,運行 play new developers_notebook就可以創(chuàng)建出一個新的名為 developers_notebook的 Web 項目。在項目目錄的父目錄下面,運行 play eclipsify developers_notebook就可以創(chuàng)建出來 Eclipse 工程。通過 Eclipse 導(dǎo)入此工程就可以在 Eclipse 里面進(jìn)行開發(fā)了。Play 框架的 support目錄下的 eclipse目錄下有個名為 org.playframework.playclipse的 Eclipse 插件,將此插件復(fù)制到 Eclipse 的 plugins目錄就可以安裝。運行 play run就可以運行此 Web 應(yīng)用,訪問 http://localhost:9000就可以看到。每次在 Eclipse 里面修改了代碼之后,不需要重新啟動應(yīng)用,只需要刷新頁面就能看到更新之后的結(jié)果。這是 Play 框架的一個非常方便的特性。
本文中的示例應(yīng)用稱為“開發(fā)人員記事本”。開發(fā)人員可以用它來記錄開發(fā)過程中的一些注意事項。下面首先介紹 Play 框架中的模型層。
模型層
模型層包含的是 Web 應(yīng)用中的領(lǐng)域?qū)ο蟆lay 框架推薦的實踐是模型層的對象不應(yīng)該是僅包含 getter/setter 方法的簡單 Java Beans,而應(yīng)該有自己的業(yè)務(wù)邏輯。Play 框架中應(yīng)用的模型層類可以是任何的 Java 類。與一般的 Java Beans 不同的是,模型層類使用聲明為 public的域作為對象的屬性。Play 框架會自動生成相應(yīng)的 getter/setter 方法。這樣可以使得代碼更加簡潔。開發(fā)人員也可以提供自己的 getter/setter 方法實現(xiàn)。
領(lǐng)域?qū)ο蟪志没?/strong>
領(lǐng)域?qū)ο蟮膶嵗话阈枰志没聛怼W畛R姷某志没绞骄褪鞘褂藐P(guān)系數(shù)據(jù)庫。Play 框架使用 JPA 規(guī)范來進(jìn)行領(lǐng)域?qū)ο蟮某志没>唧w的后臺實現(xiàn)使用的是 Hibernate。開發(fā)人員只需要使用 JPA 規(guī)范定義的標(biāo)注,就可以聲明領(lǐng)域的持久化行為。比較好的做法是將領(lǐng)域?qū)ο箢惱^承自 Play 框架提供的 play.db.jpa.Model類。play.db.jpa.Model類提供了一個域 id作為對象的標(biāo)識符,也是對應(yīng)的數(shù)據(jù)庫表中的主鍵。play.db.jpa.JPASupport類是 play.db.jpa.Model的父類,提供了一些實用方法用來完成從領(lǐng)域?qū)ο蟮綌?shù)據(jù)庫之間的映射。表 1中列出了一些重要的方法,包括常用的增刪改查操作。
表 1. play.db.jpa.JPASupport API 說明
表 1中列出的方法中,find()和 all()方法的返回值是 play.db.jpa.JPASupport.JPAQuery類的實例,表示一個領(lǐng)域?qū)ο髮嵗牟樵兘Y(jié)果。對于此查詢結(jié)果,可以進(jìn)行進(jìn)一步的操作。具體的操作,如 表 2所示。
表 2. play.db.jpa.JPASupport.JPAQuery API 說明
使用 表 2中給出的方法,就可以在領(lǐng)域?qū)ο箢愔刑砑右恍┓浅嵱玫姆椒ǎ恍枰堰@些方法添加到額外的服務(wù)層中。在示例應(yīng)用中,Note這個領(lǐng)域?qū)ο箢惐硎镜氖怯脩籼砑拥挠涗洝4a清單 1中給出了 Note類中的一些實用方法。
清單 1. 領(lǐng)域?qū)ο箢愔械膶嵱梅椒?/p>
- // 創(chuàng)建新的領(lǐng)域?qū)ο?nbsp;Note 的實例,edit() 方法的使用與 create() 類似
- Map<String, String[]> params = new HashMap<String, String[]>();
- params.put("note.title", new String[] {"My note"});
- params.put("note.content", new String[] {"My note's content"});
- Note.create(Note.class, "note", params).save();
- // 使用 find() 來進(jìn)行查找
- List<Note> notes = Note.find("byTitle", "My note").fetch();
- // 使用 findById() 來查找單個實例
- Note note1 = Note.findById(1);
- // 使用 delete() 來刪除對象實例
- Note.delete("byTitle", "My note");
- // 返回查詢結(jié)果中的第 2 到第 11 條記錄。
- Note.find("byTitle", "My note").from(1).fetch(10);
在介紹完 Play 框架的模型層之后,下面介紹控制層。
控制層
Play 框架中的控制層是模型層和視圖層之間的橋梁。控制層負(fù)責(zé)接收 HTTP 請求并返回相應(yīng)的響應(yīng)。一般來說,控制層的典型實現(xiàn)是接收到 HTTP 請求之后,從請求中獲取一些參數(shù),再調(diào)用服務(wù)層對應(yīng)的處理方法。服務(wù)層的方法會對領(lǐng)域?qū)ο筮M(jìn)行操作,完成具體的業(yè)務(wù)邏輯。最后,某種格式的響應(yīng)被返回給請求者,如 HTML 頁面、JSON 數(shù)據(jù)和 XML 數(shù)據(jù)等。Play 框架的控制層實現(xiàn)使得完成這樣的典型場景變得非常簡單。
Play 框架中的每個控制器都是一個普通的 Java 類,繼承自 play.mvc.Controller類,在包 controllers中。控制器類中的每個公開的靜態(tài)方法都表示一個動作。每個動作負(fù)責(zé)完整的請求 / 響應(yīng)的流程,也就是說,所有前面提到的所有請求 / 響應(yīng)的過程都需要在每個動作中來完成。
參數(shù)綁定
在控制層實現(xiàn)中很繁瑣但是必不可少的操作就是解析 HTTP 請求中的參數(shù)。不同的 Web 開發(fā)框架會提供自己的參數(shù)解析方式。Play 框架也提供了相應(yīng)的支持。Play 框架可以解析 HTTP 請求中查詢字符串和 URI 路徑中包含的以及請求體中以 格式編碼的參數(shù)。所有這些參數(shù)都放在 params對象中,其中包含 get()、getAll()和 put()等方法用來獲取和設(shè)置參數(shù)的值。除了這種傳統(tǒng)的使用方式之外,Play 框架還支持直接把參數(shù)的值綁定到動作方法的參數(shù)上面。比如一個動作方法的聲明是 show(String username),那么請求中的參數(shù) username的值會在 show()方法被調(diào)用時作為實際參數(shù)傳遞進(jìn)去。Play 框架會負(fù)責(zé)完成相應(yīng)的類型轉(zhuǎn)換。值得一提的是對于日期類型(java.util.Date)的參數(shù),Play 框架支持多種類型的日期格式的轉(zhuǎn)換。比如動作方法的聲明是 display(Date postedAt),而請求的格式可能是 /display?postedAt=2010-09-22,Play 框架會自動完成相應(yīng)的類型轉(zhuǎn)換。
除了常見的基本數(shù)據(jù)類型之外,Play 框架還支持直接綁定領(lǐng)域?qū)ο蟮膶嵗1热鐒幼鞣椒ǖ穆暶魇?create(Note note),可以在參數(shù)中直接指定對象實例的屬性的值。請求的格式可能是 /create?title=Note123&content=Good。Play 框架會負(fù)責(zé)創(chuàng)建一個 Note類的實例,并根據(jù)參數(shù)的值設(shè)置該實例的屬性 title和 content的值。這種綁定方式不僅支持簡單對象,還支持嵌套對象和列表。比如 /create?tags[0]=ajax&tags[1]=web可以設(shè)置列表類型屬性 tags的值。
Play 框架的這種綁定方式還支持文件對象,使得上傳文件變得非常簡單。只需要在表單中添加文件上傳的控件(<input type="file">)并使用 multipart/form-data編碼來提交請求,在動作方法的參數(shù)中就可以獲取到上傳文件對應(yīng)的 java.io.File對象。比如動作方法的聲明可能是 upload(File picture)。上傳的文件被保存在臨時目錄中,在請求完成之后會被自動刪除。可以在動作方法中完成對上傳文件的操作。
返回響應(yīng)結(jié)果
在控制層的動作方法完成了與業(yè)務(wù)邏輯相關(guān)的處理之后,需要把響應(yīng)返回給客戶端。響應(yīng)的結(jié)果可能是正確完成,也可能是出現(xiàn)錯誤。Play 框架提供了方便的實現(xiàn)用來返回不同類型的響應(yīng)。使用 play.mvc.Controller類提供的不同方法就可以生成這些響應(yīng)內(nèi)容。
◆請求正確完成,HTTP 狀態(tài)代碼為 200。使用 ok()方法生成不帶內(nèi)容的響應(yīng)。使用 render()方法來生成使用模板的響應(yīng)。使用 renderText()方法生成 text/plain類型的純文本響應(yīng)。使用 renderXml()方法生成 text/xml類型的 XML 格式的響應(yīng)。使用 renderJSON()方法生成 application/json類型的 JSON 格式的響應(yīng)。使用 renderBinary()方法生成二進(jìn)制內(nèi)容的響應(yīng)。
◆跳轉(zhuǎn)到新的頁面,HTTP 狀態(tài)代碼為 3XX。使用 redirect()方法來跳轉(zhuǎn)到新的 URL。使用 notModified()方法來返回狀態(tài)代碼 304。
◆HTTP 狀態(tài)代碼 4XX。使用 unauthorized()方法返回狀態(tài)代碼 401。使用 forbidden()方法返回狀態(tài)代碼 403。使用 notFound()方法返回狀態(tài)代碼 404。
◆服務(wù)器內(nèi)部錯誤,HTTP 狀態(tài)代碼 5XX。使用 error()方法返回狀態(tài)代碼 500。
從上面列出的方法可以看出,Play 框架使用一些有意義的方法名稱替換掉了難以記憶的 HTTP 狀態(tài)代碼,使用起來更加方便。同時,對于常見的響應(yīng)格式,包括 HTML、XML、JSON 和二進(jìn)制內(nèi)容,都提供了相應(yīng)的方法,使得開發(fā)人員不會遺漏掉響應(yīng)中 Content-Type的聲明。
方法攔截
控制層的方法通常需要執(zhí)行一些橫切的邏輯,比如用戶認(rèn)證、加載通用信息和記錄日志等。在 Spring 框架中,這些橫切的邏輯是通過面向方面編程(AOP)的支持來實現(xiàn)的。Play 框架提供了更加簡單易用的方法攔截支持,通過簡單的標(biāo)注就可以定義一些執(zhí)行攔截操作的方法。這些方法必須非公開的靜態(tài)方法。Play 框架支持的方法攔截標(biāo)注有 @Before、@After、@Finally和 @With等四種。
用 @Before標(biāo)注的方法在動作方法執(zhí)行之前被調(diào)用。@After標(biāo)注的方法在動作方法執(zhí)行之后被調(diào)用。@Finally標(biāo)注的方法在動作方法的響應(yīng)結(jié)果已經(jīng)成功生成之后被調(diào)用。這三個標(biāo)注都支持額外的兩個屬性:priority表示標(biāo)注的方法的優(yōu)先級,0 為最高;unless是一個字符串?dāng)?shù)組,表示不適用此攔截方法的動作方法的名稱。如 @Before(unless="index")表示此攔截方法不會應(yīng)用在動作方法 index()上。
如果控制器類中存在繼承體系結(jié)構(gòu)的話,父類中聲明的攔截方法對于所有子類的動作方法都是適用的。在有些情況下,開發(fā)人員可能希望把攔截方法定義在不同的類體系結(jié)構(gòu)中。由于 Java 不支持多繼承,無法通過繼承的方式來應(yīng)用來自不同類體系結(jié)構(gòu)上的攔截方法。針對這種情況,Play 框架提供了 @With標(biāo)注。在控制器類 ControllerA中定義的攔截方法可以通過 @With標(biāo)注來應(yīng)用到另外一個控制器類 ControllerB上,而且不通過繼承方式來實現(xiàn)。只需要在 ControllerB中聲明 @With(ControllerA.class)即可。
在介紹完 Play 框架的控制層之后,下面介紹視圖層。
視圖層
Web 開發(fā)框架的使用者都習(xí)慣于使用某種模板技術(shù)來生成 HTML 頁面,這些技術(shù)包括常見的 JSP、ASP 和 PHP 等。Play 框架也提供了自己的模板技術(shù),可以用來動態(tài)的創(chuàng)建 HTML、XML、JSON 以及其它文本類型的內(nèi)容。Play 框架的模板技術(shù)使用的是 Groovy 語言。Groovy 語言的靈活性和簡潔性使得 Play 框架的模板簡單而且易用。在模板中可以混用靜態(tài)內(nèi)容和生成動態(tài)內(nèi)容的各種元素。在模板中可以使用的動態(tài)元素如 表 3所示。
表 3. 模板中可用的動態(tài)元素
Play 框架中的標(biāo)簽的作用相當(dāng)于 JSP 中的標(biāo)簽。Play 框架本身提供一些常用的標(biāo)簽,開發(fā)人員也可以根據(jù)需要開發(fā)自己的標(biāo)簽。Play 框架內(nèi)置提供的標(biāo)簽說明如 表 4所示。
表 4. Play 框架提供的標(biāo)簽
在模板中可以使用來自不同地方的變量。首先是在模板生成的時候,由控制器中的動作方法通過 renderArgs對象來添加的。如 renderArgs.put("username", "Alex")就把一個變量 username添加到了模板中。其次是一些隱含的變量,如 request表示當(dāng)前的 HTTP 請求,session表示當(dāng)前的會話,params表示請求中的參數(shù)和 out表示用來輸出響應(yīng)的 java.io.Writer對象。最后就是可以通過 #{set}來設(shè)置變量。
模板的繼承
Play 框架中可以使用 #{extends}和 #{doLayout}來實現(xiàn)模板之間的繼承。模板的繼承機制對于實現(xiàn)靈活的頁面布局很有幫助。一個模板可以定義清楚頁面的基本布局結(jié)構(gòu),其它模板可以繼承此模板并添加具體的內(nèi)容。這樣就可以避免在不同模板中重復(fù)相同的頁面元素。
在父模板中可以包含任意的內(nèi)容。在需要由子模板填充的位置,使用 #{doLayout /} 進(jìn)行聲明即可。在子模板中通過 #{extends} 來聲明所繼承的模板。如 #{extends 'main.html'} 就聲明繼承自模板 main.html。當(dāng)子模板被生成之后,將包含父模板中的內(nèi)容。而子模板中只需要定義擴展的內(nèi)容即可。
自定義標(biāo)簽
Play 框架自身提供的標(biāo)簽只能解決一些常見的需求,很多時候開發(fā)人員需要根據(jù)需要開發(fā)出自己的標(biāo)簽。一個標(biāo)簽的定義非常簡單,就是一個模板文件。模板文件被存放在 app/views/tags目錄下,文件的名稱就是標(biāo)簽的名稱。在標(biāo)簽對應(yīng)的模板里面,開發(fā)人員可以添加任意的內(nèi)容。標(biāo)簽也是支持傳入?yún)?shù)的。在標(biāo)簽對應(yīng)的模板文件中可以用在參數(shù)名稱前面加上 _ 的方式來引用參數(shù)的值。比如一個標(biāo)簽在使用時的方式是 #{myTag name:'Alex' /},那么在該標(biāo)簽的模板文件中,就可以用 ${_name}來引用參數(shù) name 的值。有些標(biāo)簽是支持在使用的時候添加標(biāo)簽體的,如 #{anotherTag} 測試文字 #{/anotherTag}。對于這種情況,在標(biāo)簽的模板文件中可以用 #{doBody} 來引用標(biāo)簽體中的內(nèi)容。
在介紹完 Play 框架的視圖層之后,下面介紹 HTTP 路由。
HTTP 路由
在前面介紹過,Play 框架中的控制器用來接受 HTTP 請求并返回相應(yīng)的響應(yīng)。這個過程的重要一環(huán)就是 HTTP 請求的 URI 與控制器之間的映射關(guān)系。Play 框架提供了靈活的 HTTP 路由功能來完成這個映射。路由信息被保存在 config/routes 文件中,采用簡單的方式進(jìn)行聲明。每條路由記錄包含 3 個元素,分別是 HTTP 方法的名稱、匹配的 URI 模式以及對應(yīng)的控制器動作方法。路由記錄表示的含義是當(dāng)使用給定的 HTTP 方法來請求對應(yīng)模式的 URI 的時候,控制器動作方法就會被調(diào)用。
Play 框架支持的 HTTP 方法有 GET、POST、PUT、DELETE 和 HEAD。使用通配符 *可以匹配任何方法。在 URI 模式的聲明中可以使用正則表達(dá)式來表示復(fù)雜的映射規(guī)則。URI 模式中還可以使用 {...}來聲明動態(tài)的部分。每個動態(tài)部分都是有名稱的,可以在控制器動作方法中通過 params對象來獲取。比如,/notes/home 這樣的 URI 模式會匹配 /notes/home,但是 /notes/{id} 可以匹配 /notes/123 和 /notes/abc,而且 URI 模式中 /notes/ 后面的部分可以作為參數(shù) id 的值被獲取到。URI 模式 /notes/{<[0-9]+>id} 使用了正則表達(dá)式,只會匹配 /notes/后面緊跟的全是數(shù)字的情況。在聲明控制器的動作方法的時候,需要使用帶名稱空間的全名,如 myapp.Notes.show。有些動作方法是帶參數(shù)的,可以在聲明的時候預(yù)先綁定一些參數(shù)值,這樣可以方便的添加一些 URI 別名。比如動作方法 Notes.show() 有一個參數(shù) id 用來指明要顯示的內(nèi)容的 ID。如果參數(shù) id 的值為 0,則會顯示所有內(nèi)容的一個列表。這樣的話,就可以定義一個類似 GET /notes/all Notes.show(id:0) 的路由聲明。這樣暴露出來的 URI 更加簡潔和易于記憶。
在路由文件中的路由聲明是按照從上到下的優(yōu)先級來進(jìn)行匹配的。比較具體的 URI 模式應(yīng)該放在比較通用的模式之前。對于靜態(tài)文件,可以通過一個特殊的動作方法 staticDir 進(jìn)行聲明。比如 GET /files staticDir:files 就聲明了 files目錄中包含的是靜態(tài)文件。
在介紹完 HTTP 路由之后,下面介紹 Play 框架獨特的無狀態(tài)的體系結(jié)構(gòu)。
無狀態(tài)的體系結(jié)構(gòu)
HTTP 協(xié)議本身就被設(shè)計成無狀態(tài)的,采用請求 / 響應(yīng)的模式。不同的請求之間并不存在相互關(guān)系。但是這種架構(gòu)模式在開發(fā)某些 Web 應(yīng)用的時候不是很方便。有些應(yīng)用要求用戶進(jìn)行認(rèn)證登錄之后才能進(jìn)行某些操作。同樣的 URL,認(rèn)證和未認(rèn)證用戶看到的內(nèi)容是不同的。而且用戶認(rèn)證成功之后,他應(yīng)該在一段時間內(nèi)保持這種認(rèn)證狀態(tài)。否則的話,用戶每次都需要輸入用戶名和密碼才能訪問受限的內(nèi)容。對于這種情況,很多 Web 開發(fā)框架提供了會話的支持,允許應(yīng)用保存一些與會話相關(guān)的數(shù)據(jù)。Java Servlet 規(guī)范中的 javax.servlet.http. HttpSession就是一種會話的接口。應(yīng)用的服務(wù)器會負(fù)責(zé)維護(hù)每個會話相關(guān)的數(shù)據(jù)。這些數(shù)據(jù)可以通過一個會話 ID 來進(jìn)行標(biāo)識。這個標(biāo)識會利用瀏覽器的 cookie 機制保存在瀏覽器端,也可以作為請求 URL 的參數(shù)來傳遞。服務(wù)器端通過此標(biāo)識來識別每個會話。在處理相應(yīng)的請求的時候,就可以根據(jù)會話 ID 來獲取保存在服務(wù)器端上的會話數(shù)據(jù)。會話機制的問題是會影響應(yīng)用的可伸縮性。如果一個應(yīng)用使用多臺服務(wù)器的話,就需要額外的機制來保證同一用戶在不同機器上面的會話是同步的。而無狀態(tài)的實現(xiàn)則不存在這個問題,對于某一個請求,由不同機器來處理的結(jié)果都是相同的。
Play 框架的設(shè)計架構(gòu)就是無狀態(tài)的。它沒有提供服務(wù)器端的機制用來維護(hù)跨多個請求的數(shù)據(jù)。如果確實需要保存這樣的數(shù)據(jù)的話,可以考慮下面幾種方案:
◆保存在 Session 或 Flash 作用域中。Play 框架中仍然有會話的機制,但是并沒有提供在服務(wù)器端保存會話數(shù)據(jù)的能力。會話數(shù)據(jù)是保存在瀏覽器的 cookie 中的,由瀏覽器在每次請求的時候自動發(fā)送。通過這種方式來達(dá)到維護(hù)會話數(shù)據(jù)的目的。由于會話數(shù)據(jù)是保存在 cookie 中,其大小是有限制的,一般不能超過 4K 字節(jié),而且只能保存字符串類型的數(shù)據(jù)。Flash 作用域和會話一樣,也是通過 cookie 來保存的。所不同的是,F(xiàn)lash 作用域中的數(shù)據(jù)只在下次請求中是有效的。
◆保存在持久化的數(shù)據(jù)存儲中,如數(shù)據(jù)庫中。如果需要在多個請求中使用同一個領(lǐng)域?qū)ο蟮脑挘梢园堰@個對象的 ID 保存在 Session 或 Flash 作用域中,而在控制器動作方法中使用此 ID 來從數(shù)據(jù)庫中查詢相應(yīng)的對象。
◆保存在暫時性數(shù)據(jù)存儲中,如緩存中。Play 框架內(nèi)置了緩存的支持,通過調(diào)用類 play.cache.Cache 就可以對緩存進(jìn)行操作。與使用持久化存儲類似,緩存中的鍵的值可以保存在 Session 或 Flash 作用域中。
對于熟悉了 Java Servlet 規(guī)范的開發(fā)人員來說,需要一些時間來適應(yīng) Play 框架的這種無狀態(tài)的體系結(jié)構(gòu)。不過這種結(jié)構(gòu)對于應(yīng)用的可伸縮性來說,確實是非常有好處的。
介紹完無狀態(tài)的體系結(jié)構(gòu)之后,下面介紹一些其它話題。
其它話題
測試
Play 框架對應(yīng)用的測試也提供了良好的支持。Play 框架一共支持三種類型的測試,分別是單元測試、功能測試和界面測試。單元測試主要用來測試應(yīng)用的模型層代碼。單元測試用例的 Java 類繼承自 play.test.UnitTest ,可以使用 JUnit 4 提供的標(biāo)注和斷言。功能測試主要用來測試應(yīng)用的控制層代碼。功能測試用例的 Java 類繼承自 play.test.FunctionalTest。在測試用例中,可以通過 GET()、POST()、PUT()、DELETE() 和 makeRequest()等方法來發(fā)出 HTTP 請求,也可以直接調(diào)用控制器中的動作方法。除此之外,還可以使用一些與 HTTP 響應(yīng)相關(guān)的斷言。如 assertStatus()、assertContentType() 和 assertHeaderEquals()分別用來驗證 HTTP 狀態(tài)代碼、內(nèi)容類型和 HTTP 頭。界面測試使用 Selenium 工具來進(jìn)行。開發(fā)人員可以使用 Selenium 的語法來編寫測試用例,也可以使用 Play 框架提供的 #{selenium}標(biāo)簽。
在進(jìn)行測試的時候,需要準(zhǔn)備一些測試數(shù)據(jù)。測試數(shù)據(jù)可以用 YAML 的格式保存在文本文件中,并通過 play.test.Fixtures.laod()方法來加載這些數(shù)據(jù)到數(shù)據(jù)庫中。當(dāng)測試結(jié)束之后,可以通過 deleteAll()方法來刪除這些數(shù)據(jù)。
在測試的時候,需要用 play test命令以測試模式啟動應(yīng)用,再用瀏覽器訪問 http://localhost:9000/@tests進(jìn)行測試。
任務(wù)調(diào)度
在 Web 應(yīng)用開發(fā)中,有時候會需要定期執(zhí)行一些調(diào)度任務(wù),比如數(shù)據(jù)庫備份和數(shù)據(jù)同步等。這些任務(wù)不是通過 HTTP 請求來觸發(fā)的,而是定時執(zhí)行的。Play 框架提供了內(nèi)置的任務(wù)調(diào)度支持的能力。創(chuàng)建新任務(wù)的時候,只需要繼承自 play.jobs.Job類,并覆寫 doJob()方法即可。如果要創(chuàng)建的任務(wù)有返回結(jié)果的話,覆寫 doJobWithResult()方法即可。任務(wù)創(chuàng)建完成之后,可以選擇不同的調(diào)度方式。一種方式是在應(yīng)用啟動的時候執(zhí)行一次。只需要在任務(wù)的 Java 類上添加標(biāo)注 @OnApplicationStart即可。對于定期執(zhí)行的任務(wù),Play 框架提供了兩個標(biāo)注:一個是 @Every,用來按照固定的時間間隔調(diào)度任務(wù),如 @Every("1h")聲明任務(wù)每個小時執(zhí)行一次;另外一個是 @On,用來聲明描述調(diào)度策略的 CRON 表達(dá)式。
安全
Play 框架提供了對 Web 應(yīng)用安全性方面的支持,可以防范一些常見的攻擊方式。前面提到過,Play 框架中的會話數(shù)據(jù)是保存在瀏覽器的 cookie 中的。這些數(shù)據(jù)是經(jīng)過簽名的,可以防止被惡意攻擊者所篡改。應(yīng)用中的重要數(shù)據(jù)也不應(yīng)該保存在會話中。Play 框架中的模板在輸出 HTML 內(nèi)容的時候,會自動對內(nèi)容進(jìn)行轉(zhuǎn)義,可以防范跨站點腳本攻擊。對于 SQL 注入攻擊,開發(fā)人員應(yīng)該盡量使用提供的 find()方法來查詢領(lǐng)域?qū)ο蟆τ谧约簞?chuàng)建的查詢語句,應(yīng)該在語句中使用占位符并進(jìn)行參數(shù)綁定,而不是通過字符串相加的方式來創(chuàng)建。為了防范跨站點請求偽造,Play 框架中的控制器的動作方法都可以使用 checkAuthenticity()方法來聲明調(diào)用此方法時的請求中必須包含合法的令牌。這個令牌用來確保當(dāng)前請求是由應(yīng)用自身發(fā)出的,而不是被偽造的。通過 session.getAuthenticityToken()方法可以生成一個只對當(dāng)前會話有效的令牌,需要在請求的時候附帶此令牌。如果是通過頁面上的表單來提交請求的話,Play 框架也提供了一個標(biāo)簽 #{authenticityToken /}用來生成一個包含了令牌的隱藏域,可以直接在模板中使用。
總結(jié)
Play 框架作為一個優(yōu)秀的 Java Web 應(yīng)用開發(fā)框架,可以幫助開發(fā)人員快速高效的構(gòu)建 Web 應(yīng)用。它為開發(fā)人員提供了一個良好的基礎(chǔ)架構(gòu),并屏蔽了很多底層的實現(xiàn)細(xì)節(jié)。開發(fā)人員可以用一個簡單的視角來看待 Web 應(yīng)用開發(fā),而不需要關(guān)心過多的細(xì)節(jié)。Web 開發(fā)人員可以熟悉 Play 框架,并在開發(fā)中選用這個框架。
【編輯推薦】
- Java Web應(yīng)用開發(fā)中的一些概念
- 理想的Java Web開發(fā)框架
- Java Web Servlet學(xué)習(xí)淺談
- 新一代Java Web開發(fā)框架JSF框架
- 現(xiàn)代Java Web開發(fā)框架分析