給Java開發人員的Play Framework(2.4)介紹 Part1:Play的優缺點以及適用場景

1. 關於這篇系列

這篇系列不是Play框架的Hello World,因爲這樣的文章網上已經有很是多。javascript

這篇系列會首先結合實際代碼介紹Play的特色以及適用場景。而後會有幾篇文章介紹Play與Spring,JPA(Hibernate)的集成,以及一些Play應用的最佳實踐。 這期間會在Github上提供一個腳手架項目。方便感興趣的朋友直接動手嘗試。html

最後會簡單分析Play的部分源碼。幫助你們理解黑盒子的內部機制。java

我水平有限,有錯誤歡迎指出。jquery

2. Play介紹

Play Framework是一個開源的Web框架。背後商業公司是Typesafe。git

要介紹Play以前。首先理清Play的兩個不一樣的分支。 Play 1.x 使用Java開發。最新版本號是1.3.1,僅僅支持Java項目。github

從11年開始就進入了維護階段,新項目通常不考慮使用Play1。web

Play 2.x 使用Scala和Java開發。同一時候支持Java和Scala項目。 這裏主要介紹最新的Play2.4 for Java。有一點需要提早說明,儘管Play2主要由Scala開發,但是對於項目中的通常開發者而言, 使用Play可以全然不懂Scala。詳細狀況後面會說明。編程

3. 爲何要了解Play

現在的Web框架或者類庫可以說是浩如煙海。近十年來,在Web開發領域,JVM陣營的佔有率一直不高。 數據來源(http://hotframeworks.com/#rankings)
這是國外開源項目的數據,相對來講國內Java框架的使用率會高一些。而近期幾年,Ruby和Python在國內的開發羣體也在不斷壯大。後端

Java框架在Web領域不那麼受歡迎,主要緣由在於開發速度遠落後於其它的開發框架。對於初創公司而言,高速開發出產品投入市場試錯比花半年打磨出一款功能性能齊備的 應用更加劇要,而對於成熟產品,也需要高速響應頻繁的需求變化,這方面動態語言又更勝一籌。因此說到Web後端框架的技術選型,除非技術團隊有比較深的JVM背景。 不然會傾向於選擇RoR,Django這些框架。瀏覽器

JVM陣營在Web領域逐漸落後主要有三個緣由:編譯的鍋,技術棧的鍋和語言的鍋。

你們都知道Java源碼需要編譯以後才幹執行,直接結果是每次改動源碼都需要從新啓動Webserver才幹看到效果。

假設項目比較小類也少,從新啓動時間還勉強能接受。

我曾經參與的一個項目,使用的是WebLogicserver。Spring容器裏大概有上千個Bean,從新啓動一次至少得花5分鐘。仍是優化後的結果。工做時間至少有20%花在從新啓動上了。 儘管現在有JRebel之類的熱載入技術,但是國內使用的相對較少。

Servlet規範在1997年出現,在當時可以說是很是先進的技術。加上Tomcat的橫空出世。直接促成了JSP的崛起。然而時過境遷,Servlet風光再也不。 Web容器存在的必要性也被愈來愈多的人質疑

緣由就在於人爲的將應用與容器剝離, 儘管這樣的作法本意是好的,但是結果就是給開發測試部署帶來一系列集成的問題,現在愈來愈多的項目開始使用內嵌的Jetty或Tomcat就是一個現實的樣例。 Servlet還帶來一個問題,就是有狀態的server。一旦使用了Session,server就沒法享受到水平擴展的長處了。由此不得不採用Session複製或者粘性Session(Sticky Session)的 方案來解決問題,無論採取哪一種方案都會有性能損耗。並且推高了技術成本。

Servlet說到底是Java EE家族的一員。因爲Sun的領導(Oracle背鍋), 從Java EE 5開始。Java EE的角色已經從技術創新者轉換爲尾隨者,這些年基本上可以說是跟着開源社區的步子在走的。除了政府大單和跨國企業,你很是難再看見它的身影了。

至於語言。事實上從JDK8開始,Java已經很是好用了。

只是從JDK5到JDK8。十年太長。尤爲是在Web。

以前Java陣營受累於沒有成熟的高速開發框架。Spring熱衷於提供各類集成方案,但是配置和使用仍是至關的麻煩,直到Spring Boot的出現纔有改善。 只是近幾年出現了一些至關優秀的框架,如DropwizardPlayVert.x。 這篇系列要介紹的Play,經過ClassLoader在源碼改動的時候動態載入類,攻克了改動代碼需要從新啓動server的問題,全然拋棄了Servlet技術棧,基於Netty實現了本身的 請求響應接口(Request/Result),基於Play的應用就是無狀態的,另外Play處理請求的方式是無堵塞的(Non-Blocking)。Play2在設計的時候借鑑了RoR的不少長處。 學習Play可以讓你瞭解一些現代化框架的特色,同一時候可以爲你打開異步編程世界的大門。Promise已經被Scala,JavaScript等語言大量使用,Actor模型也已經遍地開花。 這些你都可以直接在Play中使用。或者你想保持原來的編程風格也全然沒有問題。

4. Play的特性

1. Play2的模板引擎

Play2的模板是很是強大並且easy上手的. 相對於Java領域其它模板引擎(Freemarker, Velocity, JSP, Groovy, etc), 主要有三個特色.
1) 簡單易上手, 沒有JSP裏面繁雜的內置對象和指令, 所有功能都經過方法調用完畢.
2) 主流IDE中都支持Play模板的靜態類型檢查, 類似JSP.
3) 支持反向路由.
舉個樣例, 通常系統都會有一個固定的頁面佈局, 比方分出頁頭頁尾。假設用JSP或者Velocity之類的模板。 通常都是經過sitemesh+filter或者在每個頁面include來完畢佈局。使用Play模板, 完畢這個功能很是easy。 首先定義一個main頁面 main.scala.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@(title: String = "默認標題")(staticFile: Html = Html(""))(content: Html)  <!DOCTYPE html> <html lang="zh-cmn-Hans"> <head>  <meta charset="utf-8" />  <title>@title</title>   </head>  <body>  @header() <-- 頁頭 -->  @navigator() <!-- 導航 -->  @content  <script src="@routes.Assets.versioned("js/jquery-1.11.2.min.js")"></script>  @staticFile 
1
@(title: String = "默認標題")(staticFile: Html = Html(""))(content: Html) 

這一部分是參數聲明。這裏聲明瞭三個參數:title標題, 有默認值;staticFile爲html代碼塊, 可以傳js等。content爲頁面內容。

1
2
3
@header() <-- 頁頭 -->  @navigator() <!-- 導航 --> 

這一部分是引用同文件夾下的另外兩個頁面:header.scala.html和navigator.scala.html。

爲何能這樣引用,因爲這些頁面(main,header,navigator)都會被本身主動 編譯成一個方法(準確地說是一個Scala object,只是這裏先當作方法),因此這裏至關於方法調用。相同。這個main也會被編譯成方法。其它頁面可以調用main來完畢佈局。 好比 login.scala.html

1
2
3
4
5
6
7
8
9
@main() {  <script type="text/javascript">  FG.user.login();  </script> } {  <div class="login width1200">  <!-- login -->  </div> } 

這就是一個簡單的登陸頁面。

登陸頁面調用main頁面的方法,第一個參數不傳使用默認標題。第二個參數傳入登陸頁面的js代碼,第三個參數傳入登陸頁面的html代碼。 這樣就完畢了頁面佈局, 沒有隨處可見的include, 也沒有暗箱操做的filter, 所有的一切都是方法調用, 是否是很是簡單清晰?

靜態類型檢查就不說了, 原本Java的一大長處(Que Dian)就是類型檢查,因此在Java裏用Freemarker或者Velocity這樣的模板的作法值得商榷。

反向路由的意思是, 在Play中, 所有的Controller url都配置在一個routes文件裏, 好比

1
GET /register @controllers.user.LoginController.registerPage 

以後不論是在Controller裏仍是模板中, 都不用硬編碼url。而是使用routes文件。好比在Controller中使用redirect(routes.LoginController.registerPage())就能實現重定向。

而在模板中使用 <a href="@controllers.routes.LoginController.registerPage()">來指向連接。

這樣的風格就是REST裏的URI模板。

2. 熱部署

這個上面介紹過。不用從新啓動server。

3. 內置dev/prod環境,內置部署腳本

尋常開發的時候使用run啓動Play,是跑在dev模式。 Play會定時掃描源碼文件夾進行熱更新。並且類都是訪問的時候再載入,提升啓動速度。 使用start啓動項目就執行在prod模式。Play內置dist命令。可以把所有的文件打包成一個zip,解壓以後直接執行bin文件夾下的可執行文件就能夠啓動項目。除了JDK以外無須不論什麼其它外部依賴。

這大大減輕了運維成本,同一時候也可以很是方便的進行持續集成(CI)。

4. 使用Play開發的Server大部分能作到Stateless

這個以前也說過。Play拋棄了Servlet/JSP裏Session等概念, 內置沒有提供方法將對象與server實例進行綁定(你要使用HashMap存的話Play也沒辦法)。 推薦的作法是使用外部緩存, 比方Redis, Memcached等。可能有人會以爲沒有Session是Play的一個缺點(Play裏的Session和Servlet Session不是一回事), 但是僅僅要你開發過流量大一點的應用, 你就會理解這點。

5. 好用的配置庫

假設你以前開發過Java項目, 確定寫過**.properties或者管理過一大堆的xml。Java內置庫對properties文件的處理是很是弱的,你不得不本身寫一些工具類去進行處理, 並且properties文件還不支持更復雜的語法。

Play使用Typesafe Config庫,配置文件使用HOCON格式,默認配置文件爲application.conf。

你能很是easy讀取裏面的配置, 並且你也可以把本身的配置寫在裏面。

因此項目中基本不需要使用properties或者xml文件了,除了第三方庫需要的。

6. Play插件

RoR框架之因此好用。主要緣由之中的一個就是環繞RoR有至關豐富的插件可供選擇,很是多業務功能甚至都不需要開發就能實現。

Play的插件數量固然相對於RoR仍是要少一些, 只是你遇到的需求基本都有現成的插件可以使用。比方發郵件, 受權和驗證, sitemap生成,第三方登陸等等。

本身寫一個插件也很是簡單。

7. 優秀的測試支持

因爲Play誕生的時候TDD已經很是火熱。因此Play對測試的支持很是好。

好比如下的幾行代碼就能對Controller進行測試。

1
2
3
Http.RequestBuilder request = new Http.RequestBuilder().method(POST).uri(routes.LoginController.requestPhoneCode(phone).url()); Result result = route(request); assertThat(result.status(), is(OK)); 

Play還內置了對 Selenium WebDriver的支持。可以模擬瀏覽器進行測試。如下是官方的樣例:

1
2
3
4
5
6
7
8
9
public class BrowserFunctionalTest extends WithBrowser {   @Test  public void runInBrowser() {  browser.goTo("/");  assertNotNull(browser.$("title").getText());  }  } 

8. 優秀的REST支持

Play2從誕生起就能很是easy的支持RESTful風格的架構(因爲Play2在設計的時候REST就已經大行其道), 在Play2中實現RESTful API的演示樣例可以參考Stackoverflow上的這個回答

5. 使用Play過程當中遇到的坑

1. 首次編譯速度過慢

這是Scala的鍋。Scala在編譯過程當中要經歷至少30個步驟, 致使編譯速度至關慢。在個人機器上(Core™ i5-4590 CPU @ 3.30GHz,RAM 8GB)。編譯100多個Scala類大約需要1到2分鐘。

好在sbt可以增量編譯, 即首次編譯以後,你再改動代碼。編譯器僅僅會編譯那些它以爲需要編譯的類,編譯幾個類的時候速度很是快,基本刷新頁面就能完畢。

2. IDE的Scala插件偶爾會誤報錯誤

首先得說明。最適合開發Play項目的IDE是IntelliJ IDEA

現在IDEA最新的Scala插件相比以前的版本號,已經有很是大的提高。 只是偶爾仍是會出現誤報的狀況,這個問題隨着新版本號插件的公佈應該會慢慢解決。

3. Scala和Sbt的學習成本較高

這多是初次接觸Play的用戶遇到的最大障礙。事實上對於大多數業務開發者來講。這不是問題。使用Play for Java版本號,項目代碼99%都是Java代碼, 而Sbt類似於Maven,一旦項目搭建好後不需要過多接觸,僅僅要學會幾個常用的命令就可以了,好比project root(切換項目), run(啓動server在dev模式)。

咱們團隊大部分紅員以前都沒有接觸過Scala和Play,通過一兩週的磨合期以後都能很是順利的使用Play進行開發了。

4. Play的API變化速度比較快

Play的版本號號遵循Semantic Versioning,不一樣主版本號的API變化很是大。比方Play1和Play2就是兩個不一樣的框架。 而副版本號之間API也會有一些變化,並且不必定全然向後兼容。好比使用Play2.3.x的項目在升級到2.4的時候,需要依照官方提供的遷移手冊進行代碼改動, 否則是執行不了的。

這對於其它背景的開發者來講可能比較easy理解,但是假設是一直習慣於使用Spring MVC或Struts2的話,可能會對這點感到不適。

6.總結

Play2可以算是一個現代化的框架,吸取了RoR諸多長處。同一時候又攻克了Java開發中的一些痛點,在國外已經被大量使用。

參見 數據來源(http://www.infoq.com/research/jvm-web-frameworks)

Play和Spring MVC的定位有些類似。但是比Spring MVC提供更豐富的功能,和Web有關的項目都可以使用Play。但是假設要用好Play,對團隊有必定的要求。

首先,你的團隊應該不是墨守成規的團隊。

大部分人都懼怕變化,這是不爭的事實。

JDK的發展緩慢加上國內的技術氛圍,着實讓Java開發者過了幾年的舒服日子。

你假設是05年學會了ibatis和Spring。而後這十年去環遊世界了,在15年你照樣能輕鬆找到一份待遇還算可以的工做。然而事情已經開始發生變化,不會學習可能會被淘汰。

其次。你的團隊應該重視工做效率和質量,並且有時間作出改進。國內很是多團隊信奉的是人海戰術。

以低薪聘請大量不合格的開發者來開發業務功能。 而不是注重單人的工做效率和質量,很是多項目的加班和延期都源於此。這樣的團隊就不適合用Play。很是難想象天天都要加班去應付工做的團隊有時間打磨升級本身的工具和技能。

但是反過來低效率的工具和技能又拖累了本身的工做效率。這是一個惡性循環。

最後。團隊中需要有人對Scala和Sbt有必定的瞭解。儘管Play有Java版本號可以使用,但是假設不會Scala和Sbt,在搭建好開發環境。使用一些高級功能(如Filter)的時候可能會遇到麻煩。

下篇我會介紹Play和Spring還有JPA(Hibernate)的集成,畢竟Spring在大部分Java項目仍是主流。有問題和建議歡迎指出。

 

本文借鑑:http://skaka.me/blog/2015/07/27/play1/

相關文章
相關標籤/搜索