這篇系列不是Play框架的Hello World,因為這樣的文章網上已經有很多。
這篇系列會首先結合實際代碼介紹Play的特點以及適用場景,然后會有幾篇文章介紹Play與SPRing,JPA(Hibernate)的集成,以及一些Play應用的最佳實踐, 這期間會在Github上提供一個腳手架項目,方便感興趣的朋友直接動手嘗試。最后會簡單分析Play的部分源碼,幫助大家理解黑盒子的內部機制。
我水平有限,有錯誤歡迎指出。
2. Play介紹Play Framework是一個開源的Web框架,背后商業公司是Typesafe。要介紹Play之前,首先理清Play的兩個不同的分支。 Play 1.x 使用Java開發,最新版本是1.3.1,只支持Java項目。從11年開始就進入了維護階段,新項目一般不考慮使用Play1。 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源代碼需要編譯之后才能運行,直接結果是每次修改源代碼都需要重啟Web服務器才能看到效果。如果項目比較小類也少,重啟時間還勉強能接受。 我以前參與的一個項目,使用的是WebLogic服務器,Spring容器里大概有上千個Bean,重啟一次至少得花5分鐘,還是優化后的結果。工作時間至少有20%花在重啟上了。 雖然現在有JRebel之類的熱加載技術,但是國內使用的相對較少。
Servlet規范在1997年出現,在當時可以說是很先進的技術,加上Tomcat的橫空出世,直接促成了jsp的崛起。然而時過境遷,Servlet風光不再, Web容器存在的必要性也被越來越多的人質疑。原因就在于人為的將應用與容器剝離, 雖然這種做法本意是好的,但是結果就是給開發測試部署帶來一系列集成的問題,現在越來越多的項目開始使用內嵌的Jetty或Tomcat就是一個現實的例子。 Servlet還帶來一個問題,就是有狀態的服務器。一旦使用了session,服務器就無法享受到水平擴展的好處了,由此不得不采用Session復制或者粘性Session(Sticky Session)的 方案來解決這個問題,無論采取哪種方案都會有性能損耗,并且推高了技術成本。Servlet說到底是Java EE家族的一員,由于Sun的領導(Oracle背鍋), 從Java EE 5開始,Java EE的角色已經從技術創新者轉換為跟隨者,這些年基本上可以說是跟著開源社區的步子在走的,除了政府大單和跨國企業,你很難再看見它的身影了。
至于語言,其實從JDK8開始,Java已經很好用了。不過從JDK5到JDK8,十年太長,尤其是在Web。
之前Java陣營受累于沒有成熟的快速開發框架,Spring熱衷于提供各種集成方案,可是配置和使用還是相當的麻煩,直到Spring Boot的出現才有改善。 不過近幾年出現了一些相當優秀的框架,如Dropwizard,Play,Vert.x。 這篇系列要介紹的Play,通過ClassLoader在源代碼修改的時候動態加載類,解決了修改代碼需要重啟服務器的問題,完全拋棄了Servlet技術棧,基于Netty實現了自己的 請求響應接口(Request/Result),基于Play的應用就是無狀態的,另外Play處理請求的方式是無阻塞的(Non-Blocking)。Play2在設計的時候借鑒了RoR的許多優點, 學習Play能夠讓你了解一些現代化框架的特點,同時能夠為你打開異步編程世界的大門。Promise已經被Scala,JavaScript等語言大量使用,Actor模型也已經遍地開花, 這些你都可以直接在Play中使用,或者你想保持原來的編程風格也完全沒有問題。
4. Play的特性1. Play2的模板引擎Play2的模板是很強大并且容易上手的. 相對于Java領域其他模板引擎(Freemarker, Velocity, JSP, Groovy, etc), 主要有三個特點.1) 簡單易上手, 沒有JSP里面繁雜的內置對象和指令, 所有功能都通過方法調用完成.2) 主流IDE中都支持Play模板的靜態類型檢查, 類似JSP.3) 支持反向路由.舉個例子, 一般系統都會有一個固定的頁面布局, 比如分出頁頭頁尾。如果用JSP或者Velocity之類的模板, 一般都是通過sitemesh+filter或者在每個頁面include來完成布局。使用Play模板, 完成這個功能非常容易。 首先定義一個main頁面 main.scala.html:
12345678910111213141516171819202122 | |
1 | |
這一部分是參數聲明,這里聲明了三個參數:title標題, 有默認值;staticFile為html代碼塊, 可以傳js等;content為頁面內容。
123 | |
這一部分是引用同目錄下的另外兩個頁面:header.scala.html和navigator.scala.html。為什么能這樣引用,因為這些頁面(main,header,navigator)都會被自動 編譯成一個方法(準確地說是一個Scala object,不過這里先當做方法),所以這里相當于方法調用。同樣,這個main也會被編譯成方法,其他頁面可以調用main來完成布局, 例如 login.scala.html
123456789 | |
這就是一個簡單的登錄頁面。登錄頁面調用main頁面的方法,第一個參數不傳使用默認標題,第二個參數傳入登錄頁面的js代碼,第三個參數傳入登錄頁面的html代碼。 這樣就完成了頁面布局, 沒有隨處可見的include, 也沒有暗箱操作的filter, 所有的一切都是方法調用, 是不是很簡單清晰?
靜態類型檢查就不說了, 本來Java的一大優點(Que Dian)就是類型檢查,所以在Java里用Freemarker或者Velocity這種模板的做法值得商榷。
反向路由的意思是, 在Play中, 所有的Controller url都配置在一個routes文件中, 例如
1 | |
之后無論是在Controller里還是模板中, 都不用硬編碼url。而是使用routes文件。例如在Controller中使用redirect(routes.LoginController.registerPage())就能實現重定向。 而在模板中使用<a href="@controllers.routes.LoginController.registerPage()">來指向鏈接。這種風格就是REST里的URI模板。
這個上面介紹過,不用重啟服務器。
3. 內置dev/prod環境,內置部署腳本平常開發的時候使用run啟動Play,是跑在dev模式。 Play會定時掃描源碼目錄進行熱更新,并且類都是訪問的時候再加載,提高啟動速度。 使用start啟動項目就運行在prod模式。Play內置dist命令,可以把所有的文件打包成一個zip,解壓之后直接運行bin目錄下的可執行文件即可啟動項目,除了JDK之外無須任何其他外部依賴。 這大大減輕了運維成本,同時也能夠很方便的進行持續集成(CI)。
4. 使用Play開發的Server大部分能做到Stateless這個之前也說過,Play拋棄了Servlet/JSP里Session等概念, 內置沒有提供方法將對象與服務器實例進行綁定(你要使用HashMap存的話Play也沒辦法)。 推薦的做法是使用外部緩存, 比如Redis, Memcached等??赡苡腥藭X得沒有Session是Play的一個缺點(Play里的Session和Servlet Session不是一回事), 但是只要你開發過流量大一點的應用, 你就會理解這點。
5. 好用的配置庫如果你之前開發過Java項目, 肯定寫過**.properties或者管理過一大堆的xml。Java內置庫對properties文件的處理是很弱的,你不得不自己寫一些工具類去進行處理, 而且properties文件還不支持更復雜的語法。Play使用Typesafe Config庫,配置文件使用HOCON格式,默認配置文件為application.conf。 你能很容易讀取里面的配置, 并且你也可以把自己的配置寫在里面。所以項目中基本不需要使用properties或者xml文件了,除了第三方庫需要的。
6. Play插件RoR框架之所以好用,主要原因之一就是圍繞RoR有相當豐富的插件可供選擇,很多業務功能甚至都不需要開發就能實現。Play的插件數量當然相對于RoR還是要少一些, 不過你遇到的需求基本都有現成的插件可以使用。比如發郵件, 授權和驗證, sitemap生成,第三方登錄等等。自己寫一個插件也很簡單。
7. 優秀的測試支持因為Play誕生的時候TDD已經很火熱,所以Play對測試的支持非常好。 例如下面的幾行代碼就能對Controller進行測試。
123 | |
Play還內置了對 Selenium WebDriver的支持,可以模擬瀏覽器進行測試。以下是官方的例子:
123456789 | |
Play2從誕生起就能很容易的支持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?,F在IDEA最新的Scala插件相比之前的版本,已經有很大的提升。 不過偶爾還是會出現誤報的情況,這個問題隨著新版本插件的發布應該會慢慢解決。
3. Scala和Sbt的學習成本較高這可能是初次接觸Play的用戶遇到的最大障礙。其實對于大多數業務開發人員來說,這不是問題。使用Play for Java版本,項目代碼99%都是Java代碼, 而Sbt類似于Maven,一旦項目搭建好后不需要過多接觸,只要學會幾個常用的命令就可以了,例如project root(切換項目), run(啟動服務器在dev模式)。 我們團隊大部分成員之前都沒有接觸過Scala和Play,經過一兩周的磨合期之后都能很順利的使用Play進行開發了。
4. Play的API變化速度比較快Play的版本號遵循Semantic Versioning,不同主版本的API變化非常大,比如Play1和Play2就是兩個不同的框架。 而副版本之間API也會有一些變化,而且不一定完全向后兼容。例如使用Play2.3.x的項目在升級到2.4的時候,需要按照官方提供的遷移手冊進行代碼修改, 不然是運行不了的。這對于其他背景的開發者來說可能比較容易理解,但是如果是一直習慣于使用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)的時候可能會遇到麻煩。
碼農必須要加班?NO!
知道碼農們都想擺脫加班狗、外賣臉的稱號,所以我們來了!
我們做了一個能讓程序員之間共享知識技能的APP,覺得可以顛覆程序員的工作方 式!
有人說我們癡心妄想,但我們不那么認為。
為了能煽爛說我們癡心妄想的人的臉,現在我們急需程序員業內的牛嗶-人物來給 我們“號脈”!“診斷費”豐厚!畢竟我們不差錢兒,只是想做到最好!
圈圈字典中講到,牛嗶-人物是指群成員數高于1000人的QQ群主或關注人數高于 2000人的貼吧吧主或粉絲人數高于10000人的微博博主或成員數高于2000主題貼的版主 或單帖閱讀量高于2000博客主或人脈超級廣的圈內紅人。
對于未能達標的未來大神們,我們只能含淚表示:蜀黍,咱們來日方長,這次暫 時不約好嗎?待他日你立地成神,我必生死相依!
來?還是不來?
圈圈互動 接頭暗號:1955246408 (QQ)
新聞熱點
疑難解答