Day 30: Play Framework —— Java开发者的梦想框架
今天是最后一天,我決定學(xué)習(xí)一下 Play 框架。原本是想寫關(guān)于Scala的,學(xué)習(xí)了幾個(gè)小時(shí)之后發(fā)現(xiàn)在一天之內(nèi)是不可能完成Scala的,所以今天會(huì)介紹一下Play框架的基本知識(shí),然后學(xué)習(xí)如何用它開發(fā)應(yīng)用。
什么是 Play 框架?
Play是一個(gè)開源的現(xiàn)代web框架,用于編寫Java和Scala的可擴(kuò)展Web應(yīng)用程序。它通過自動(dòng)重載變化來提高生產(chǎn)力,由于設(shè)計(jì)的就是一個(gè)無狀態(tài)、無阻塞的架構(gòu),所以用Play框架來編寫橫向擴(kuò)展Web應(yīng)用程序是很容易的。
為什么要用它?
我的原因是:
開發(fā)人員生產(chǎn)力:我已經(jīng)寫了8年的Java,但在過去的幾個(gè)月里我把更多的時(shí)間花在了Python和JavaScript (Node.js) 上。用動(dòng)態(tài)語言工作時(shí)最讓我吃驚的,就是用它編寫程序的速度是如此之快。Java EE和Spring框架并不是快速原型和開發(fā)的理想選擇,但在用Play框架時(shí),你更改一處刷新一下頁面,更新會(huì)立即出現(xiàn),而且它支持熱重載所有的Java代碼、模板等,可以讓你的迭代快很多。
天性使然:Play框架是建立在Netty之上的,所以它支持非阻塞I/O,這使得并行遠(yuǎn)程調(diào)用容易了很多,這一點(diǎn)對(duì)面向服務(wù)的架構(gòu)中的高性能應(yīng)用程序是很重要的。
支持Java和Scala:Play框架是一個(gè)真正的多語種Web框架,開發(fā)者可以在項(xiàng)目中同時(shí)使用Java和Scala。
一流的REST JSON支持:它很容易編寫基于REST的應(yīng)用。對(duì)HTTP路由有很好的支持,HTTP路由會(huì)將HTTP請(qǐng)求轉(zhuǎn)化為具體動(dòng)作;JSON編組/解組API是??目前的核心API,所以沒有必要加一個(gè)庫來做到這一點(diǎn)。
應(yīng)用類型案例
今天的介紹中,將開發(fā)一個(gè)社交書簽應(yīng)用程序,它允許用戶發(fā)布和共享鏈接。你可以在這里查看正在運(yùn)行的該程序,因?yàn)檫@個(gè)和第22天的應(yīng)用是一樣的,所以請(qǐng)參閱之以便更好地了解這個(gè)案例。
開發(fā)Play應(yīng)用
請(qǐng)參閱文檔以了解如何安裝Play框架,開始應(yīng)用程序的開發(fā)吧。
$ play new getbookmarks__ __ | | __ _ _ _ | '_ \| |/ _' | || | | __/|_|\____|\__ / |_| |__/play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_25), http://www.playframework.comThe new application will be created in /Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarksWhat is the application name? [getbookmarks] > Which template do you want to use for this new application? 1 - Create a simple Scala application2 - Create a simple Java application> 2 OK, application getbookmarks is created.Have fun!如上鍵入命令后,該框架會(huì)問幾個(gè)問題。首先它要求有應(yīng)用程序的名稱,然后問是否要?jiǎng)?chuàng)建一個(gè)Scala或Java應(yīng)用程序。默認(rèn)情況下,它會(huì)使用文件夾名稱作為應(yīng)用程序的名稱。
上面的命令將創(chuàng)建一個(gè)新的目錄getbookmarks并生成以下文件和目錄:
通過如下命令發(fā)布play控制臺(tái),運(yùn)行Play編寫的默認(rèn)程序。
$ cd getbookmarks $ play [info] Loading project definition from /Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks/project [info] Set current project to getbookmarks (in build file:/Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks/)__ __ | | __ _ _ _ | '_ \| |/ _' | || | | __/|_|\____|\__ / |_| |__/play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_25), http://www.playframework.com> Type "help play" or "license" for more information. > Type "exit" or use Ctrl+D to leave this console.[getbookmarks] $ run [info] Updating {file:/Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks/}getbookmarks... [info] Resolving org.fusesource.jansi#jansi;1.4 ... [info] Done updating.--- (Running the application from SBT, auto-reloading is enabled) ---[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000(Server started, use Ctrl+D to stop and go back to the console...)現(xiàn)在可以在 http://localhost:9000 里運(yùn)行該應(yīng)用了。
創(chuàng)建Story域類
該應(yīng)用程序只有一個(gè)域類 (domain class),叫做story,創(chuàng)建一個(gè)新的包模型和Java類。
package models;import play.db.ebean.Model;import javax.persistence.Entity; import javax.persistence.Id; import java.util.Date;@Entity public class Story extends Model{@Idprivate String id;private String url;private String fullname;private Date submittedOn = new Date();private String title;private String text;private String image;public Story() {}public Story(String url, String fullname) {this.url = url;this.fullname = fullname;}public Story(String url, String fullname, String image, String text, String title) {this.url = url;this.fullname = fullname;this.title = title;this.text = text;this.image = image;}// Getter and Setter removed for brevity }上述代碼定義了一個(gè)簡(jiǎn)單的JPA實(shí)體,并使用 @Entity 和 @Id JPA注解,Play用它自己的一個(gè)被稱作Ebean的ORM層,而且每一個(gè)實(shí)體類必須擴(kuò)展基本模型類。
Ebean默認(rèn)禁用,啟用它需要打開application.conf并取消注釋以下行。
ebean.default="models.*"啟用數(shù)據(jù)庫
啟動(dòng)應(yīng)用程序的數(shù)據(jù)庫,Play框架提供了內(nèi)置的H2數(shù)據(jù)庫的支持。要啟用它,打開application.conf文件,并取消如下兩行的注釋。
db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play"刷新瀏覽器會(huì)看到:
點(diǎn)擊Apply this script now將SQL的更改部署上去。
定義應(yīng)用程序的路由
今天講的應(yīng)用程序和第22天是一樣的,都有AngularJS后臺(tái)和REST后端,所以可以使用Play框架重寫REST后臺(tái)和AngularJS后端,在conf/routes文件,復(fù)制并粘貼如下代碼。
# Routes # This file defines all application routes (Higher priority routes first) # ~~~~# Home page GET / controllers.Assets.at(path="/public", file="/index.html") GET /api/v1/stories controllers.StoryController.allStories() POST /api/v1/stories controllers.StoryController.submitStory() GET /api/v1/stories/:storyId controllers.StoryController.getStory(storyId)# Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.at(path="/public", file)上述代碼表示:
創(chuàng)建Story控制器
在控制器包里創(chuàng)建一個(gè)Java類,將如下代碼粘貼進(jìn) StoryController.java 文件里。
package controllers;import com.fasterxml.jackson.databind.JsonNode; import models.Story; import play.api.libs.ws.Response; import play.api.libs.ws.WS; import play.db.ebean.Model; import play.libs.Json; import play.mvc.BodyParser; import play.mvc.Controller; import play.mvc.Result; import play.mvc.Results; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration;import java.util.List; import java.util.concurrent.TimeUnit;public class StoryController {public static Result allStories(){List<Story> stories = new Model.Finder<String , Story>(String.class, Story.class).all();return Results.ok(Json.toJson(stories));}@BodyParser.Of(BodyParser.Json.class)public static Result submitStory(){JsonNode jsonNode = Controller.request().body().asJson();String url = jsonNode.findPath("url").asText();String fullname = jsonNode.findPath("fullname").asText();JsonNode response = fetchInformation(url);Story story = null;if(response == null){story = new Story(url,fullname);}else{String image = response.findPath("image").textValue();String text = response.findPath("text").textValue();String title = response.findPath("title").textValue();story = new Story(url,fullname, image , text , title);}story.save();return Results.created();}public static Result getStory(String storyId){Story story = new Model.Finder<String, Story>(String.class, Story.class).byId(storyId);if(story == null){return Results.notFound("No story found with storyId " + storyId);}return Results.ok(Json.toJson(story));}private static JsonNode fetchInformation(String url){String restServiceUrl = "http://gooseextractor-t20.rhcloud.com/api/v1/extract?url="+url;Future<Response> future = WS.url(restServiceUrl).get();try {Response result = Await.result(future, Duration.apply(30, TimeUnit.SECONDS));JsonNode jsonNode = Json.parse(result.json().toString());return jsonNode;} catch (Exception e) {e.printStackTrace();return null;}}}上述代碼會(huì)操作:
它定義allStories()方法,該方法會(huì)找到數(shù)據(jù)庫中所有的story。它是使用Model.Finder API來做到這一點(diǎn)的,然后把story列表轉(zhuǎn)換成JSON格式并返回結(jié)果,返回HTTP狀態(tài)代碼200(即確定)。
submitStory()方法首先會(huì)從JSON讀取URL和全名的字段,然后發(fā)送GET請(qǐng)求到'http://gooseextractor-t20.rhcloud.com/api/v1/extract?url',這樣就會(huì)找出標(biāo)題、摘要以及已經(jīng)給定url的主要image。創(chuàng)建一個(gè)使用所有信息的story并保存在數(shù)據(jù)庫中,返回HTTP狀態(tài)代碼201(即創(chuàng)建)。
getStory()方法為給定的storyId獲取story,把這個(gè)story轉(zhuǎn)換成JSON格式并返回響應(yīng)。
可以從我的github倉庫下載AngularJS前端,用其中一個(gè)庫更換公共目錄。
現(xiàn)在可以訪問http://localhost:9000/看結(jié)果了。
30天系列的文章就此結(jié)束,非常感謝大家的持續(xù)關(guān)注,也希望你們能和SegmentFault一塊成長(zhǎng)。
So, see you next year. Oh~ Happy New Year to Y'all !
原文 Day 30: Play Framework--A Java Developer Dream Framework
翻譯整理 SegmentFault
總結(jié)
以上是生活随笔為你收集整理的Day 30: Play Framework —— Java开发者的梦想框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Day 29:编写你的第一个 Googl
- 下一篇: Stanford UFLDL教程 梯度检