[譯] 第三十天:Play Framework - Java開發者求之不得的框架

前言

30天挑戰的最後一天,我決定學習Play Framework.我原本想寫Sacla,可是研究幾個小時後,我發現無法在一天內公正評價Scala,下個月花些時間來了解並分享經驗。本文咱們先來看看Play框架基礎,再開發個程序。css

 

什麼是Play框架?

Play是一個開源的現代web框架,用Java和Scala寫可擴展的web程序。它能自動加載更新使得極大提升生產率。Play設計了無狀態,非阻塞的架構,這使得用Play框架開發水平擴展web程序很容易。 html

我爲何關注Play?

我學習Play的緣由:前端

  1. 高效: 我用Java已經8年了,可是幾個月前我更多關注到Python和JavaScript(Node.js).最令我吃驚的是,使用動態語言開發程序是那麼快。Java      EE和Spring框架都不是快速原型開發的理想選擇,可是用Play框架,有更新時,刷新頁面,瞧!直接能夠看到更新了。它支持全部Java代碼,模板等的熱加載,讓你快速繼續。
  1. 天然反應:Play框架基於Netty構建,因此它支持非阻塞I/O, 能簡單經濟的水平遠程調用,這對於以服務爲導向的架構高效工做很重要。
  1. 同時支持Java和Scala: Play框架是真正的通曉多語言的web框架,開發者在他們的項目中能夠同時使用Java和Scala.
  1. 首個支持REST JSON類:Play讓寫RESTful程序變得很簡單,很好的支持HTTP路由,HTTP路由將HTTP請求轉換成具體動做,JSON      marshalling/unmarshalling API在覈心API中,因此不須要添加庫來完成。 

程序用例

本文,咱們來開發一個網摘程序,容許用戶發佈和分享連接,你能夠在OpenShift上看在線程序。這和咱們以前第22天開發的程序同樣,你能夠參考以前的用例來更好了解。 java

安裝Play

請參考文檔瞭解怎樣安裝Play框架。 git

開發Play程序

介紹完基礎,咱們來開始寫程序。 github

在你機器上運行如下命令。web

$ play new getbookmarks 
       _

 _ __ | | __ _ _  _

| '_ \| |/ _' | || |

|  __/|_|\____|\__ /

|_|            |__/ 

play 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_25), http://www.playframework.com 

The new application will be created in /Users/shekhargulati/dev/challenges/30days30technologies/day30/blog/getbookmarks

What is the application name? [getbookmarks]
>
Which template do you want to use for this new application?
  1             - Create a simple Scala application
  2             - Create a simple Java application
> 2
OK, application getbookmarks is created.
Have fun!
View Code

如上,輸入命令後,Play框架會問幾個問題。首先是程序的名字,而後是咱們是否想建立一個Scala程序或者Java程序。默認使用文件夾名做爲程序名,咱們選擇第二個選項來建立Java程序。 數據庫

以上命令新建了一個目錄getbookmarks,生成一下文件和目錄。json

  1. app目錄包含了程序特定代碼如控制器,視圖和模塊。控制器包包含Java代碼,用於響應url路由。views目錄包含了服務端的模板,models目錄包含程序域模塊,在這個程序裏,域是一個Story類。
  1. conf目錄包含程序配置和路由定義文件。
  2. project目錄包含構建腳本,這個構建系統基於sbt.
  3. public包含公共資源,如css,      JavaScript和圖像目錄。
  4. test包含程序測試。 

如今咱們能夠運行Play建立的默認程序,打開play控制檯,運行play命令,而後用run命令。後端

$ 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...)
View Code

 如今能夠看到程序  http://localhost:9000.

 

建立Story域類

這個程序,咱們只有一個域類, Story. 新建一個包模塊而後新建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{ 
    @Id
    private 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
}
View Code

 以上代碼定義了一個簡單的JPA實體,用了@Entity和Id JPA註釋,Play用它本身的ORM層Ebean, 每一個實體類都擴展基礎模塊類。 

Ebean默認是沒激活的,要激活它,打開程序conf,  取消下面這行的註釋。

ebean.default="models.*"

激活數據庫

如今來激活程序的數據庫,Play框架提供了內置支持的H2數據庫,要激活它,打開程序conf文件,取消如下兩行的註釋。

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"

如今刷新瀏覽器能夠看到以下異常。

 

點擊Apply this script now使SQL更新生效。 

定義程序路由

本文,咱們開發了和第22天相同的程序,這個程序有AngularJS後端和REST後端,咱們用Play框架寫REST後端,從新用AngularJS後端,在conf/routes文件裏,複製粘貼一下代碼。

# 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)
View Code

以上代碼:

  1. 當用戶發出GET請求到'/' url, index.html會被加載。
  2. 當用戶發出GET請求到'/api/v1/stories', 會獲得全部JSON格式的文章。
  3. 當用戶發出POST請求到'/api/v1/stories', 一個新的文章會被建立。
  4. 當用戶發出GET請求到'/api/v1/stories/123', id是123的文章會被加載。 

建立StoryController

如今在控制器包裏新建Java類,複製粘貼如下代碼到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;
        } 
    } 
}
View Code

以上代碼:

  1. 定義了allStories()方法,用於在數據庫中查找文章,它用Model.Finder      API完成,而後咱們轉換這個列表成JSON格式,返回結果。返回HTTP狀態代碼200表明OK.
  1. submitStory()方法先從JSON讀取url和全名,而後發出GET請求到'http://gooseextractor-t20.rhcloud.com/api/v1/extract?url',      從給定url查找標題,摘要和主要圖像。用這些全部信息新建文章保存到數據庫,返回HTTP狀態代碼201表明已建立。
  1. getStory()方法從給定的文章id獲取文章,咱們轉換文章稱JSON格式而後在響應裏返回。 

AngularJS前端

我決定從新用第22天寫的AngularJS前端,第22天展現了咱們怎樣用AngularJS和Java Spring框架,最好的部分是用JavaScript MV*框架,若是你的程序仍是用REST接口客戶端需求,你能夠再用前端代碼。詳情參考第22天文章。 

如今刷新瀏覽器訪問程序 http://localhost:9000/

 

這就是今天的內容,但願你喜歡這個系列。 

原文:https://www.openshift.com/blogs/day-30-play-framework-a-java-developer-dream-framework

相關文章
相關標籤/搜索