1简介
Play!是一个full-stack(全栈的)Java Web应用框架,包括一个简单的无
状态MVC模型,具有Hibernate的对象持续,一个基于Groovy的模板引擎,以及建立一个现代Web应用所需的所有东西。
Play 是一个纯 Java 的框架,它让你保持使用你喜欢的开发工具和类库。如果你已经是一个使用 Java 平台的开发者,那么你不需要切换到另一种语言,其他 IDE 或者其他类库, 而仅仅是切换到一个效率更高的 Java 环境。
Play!的关键特性:
1、一个非常简单的开发周期。此框架自动编译和重新装载源文件的任何改变。
2、智能捆绑HTTP参数到Java方法参数。
3、基于Jboss Netty框架的快速HTTP服务器,使Play能支持高并发、长连接以及静态文件优秀的输出能力。
4、一个基于Groovy的强大的模板引擎,具有多层继承,定制用户标签的能力,高可拓展性等。
5、优秀的错误报告功能:当发生异常,此框架会直接显示出错代码,甚至是模板代码。
=========================================================================
颠覆臃肿的JavaEE开发框架(bloated Enterprise Java stacks)的Play框架1.0发布,它在很多方面有其革命性的独创,也有助于我们了解现在JavaEE框架的不足。
Play框架吸收PHP RUBY动态语言的特点,采取即时源码编写,即时激活,框架本身融合了编译器和服务器。取代了 compile-package-deploy 过程,提高产品的开发效率。Play框架甚至提供在线编辑器,在线修改BUG后即时投入应用。
主要架构特点:
1、一个非常简单的开发周期。此框架自动编译和重新装载源文件的任何改变。
2、简单的无状态MVC架构:智能捆绑HTTP参数到Java方法参数。
(Play框架认为一边是数据库保存状态,一边是浏览器也可以保存状态,那么还要中间件MVC保存Session状态干什么呢?
HttpSession有很多问题,虽然可以处理针对某个用户的状态,但是万一用户中途离开怎么办,HttpSession对资源消耗,以及在可伸缩性方 面是有问题的。Play框架秉承share nothing架构思想,不再象黑客那样破解原本自然正常Http模型,然后强行植入状态,无状态架构可以并行同时输出多个页面,提高Web性能。)
3、内置基于Apache Mina的快速HTTP服务器。
4、一个基于Groovy的强大的模板引擎,具有多层继承,定制用户标签的能力,Play框架认为JSP & Expression Language模板机制很好,但是需要太多配置,吸收其模板设计,剔除配置。等。
5、优秀的错误报告功能:当发生异常,此框架会直接显示出错代码,甚至是模板代码。
6 、RESTFul
众所周知的Servlet API 和Struts其实是扭曲的,使用奇怪的API将Http协议隐藏起来,Play框架认为一个Web应用框架应该给用完整的直接的对Http调用和使用,这其实就是RESTFul精神。
这样 URI是play framework的主要概念。
对一个Java对象的调用,不是写Java语句,而是使用URI就可以,如下:
GET /clients/{id} 实际是调用Clients对象的show方法。
7、集成JPA 持久层
(Play框架采取JPA作为持久化,并且使其更方便使用。个人意见:这段代码倒是直接将持久层和表现层直接耦合在一起,没看到Domain Model了。看来DDD需要普及到每个角落不容易啊。)
8、整合了缓存支持,可以使用memcached作为分布式缓存。
9、融入了OpenID 这样单点登录SSO技术。
10、提出组件重用,可以重用各种组件,包括CSS Javascript
个人点评:总体来说,Play框架是一个与Struts2 JSF Tapestry竞争的框架,但是又整合了持久层和服务器。
业务系统市场分析:
90%的web业务开源系统都是php版本的,特别是新兴产品的开源实现,java的身影几乎销声匿迹了。可惜j要ava出身的人去阅读php风格的代码简直是一种受虐。这其实说明一个道理,java优美的架构还是很有价值的。但优美+务实的平衡才是最佳选择。
框架对比:
运行方式与服务器兼容性:
Play 应用可使用这么几种方式运行:标准Servlet容器、独立服务器、Google App Engine、Stax 云计算平台,等等
你也可以将应用发布到标准的应用服务器上执行,大部分应用程序支持Play应用。
下面的应用服务器都可以用来运行Play应用。
这些应用服务器经过测试是支持Play 1.0.2的,但其他版本尚未经过充分测试。
JBoss 4.2.x | JBoss 5.x | JBoss 6M2 | Glasshfish v3 | IBM Websphere 6.1 | IBM Websphere 7 | Geronimo 2.x | Tomcat 6.x | Jetty 7.x | Resin 4.0.5 |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
详细分析与外界介绍:
下图是 Play Framework 框架中用到所有的 jar 包的集合,位于 {play}/framework/lib 目录
Play! Framework 是07年的一个项目,08年开源,09年11月25日发布了1.0版。发布后我就一直在学习这个框架。现在正式发布版本已经是1.01版,而且1.1版本也在每日更新。可以在http://download.playframework.org 下载已发布版本,和每日的最新版。
bc. # Additional modules
# ~~~~~
mdoule.gwt=../gwt
module.cms=../cms
module.forum=../forum
module.directory=../directory
学习Play!的过程中,最经常的感受就是——简直太简单了!并不是说Play!是一个设计简单的框架,相反学习中发现处处都会发现Play!设计 的完整,这种完整性甚至包括网站设计和学习文档。Play!的简单之处在于它学习和使用起来非常简单。使用Play!新建项目,所有的目录结构都会自动建 立。Play!摒弃了传统的JSP,Servlet技术(这太伟大了),自己提供了一套非常易用的MVC 框架。Play!内建了JPA的支持,内置了Hibernate作为默认的持久化引擎。
Play! 还内置了HSQLDB 数据库,支持内存数据库,非常方便做项目开发和测试。
Play!的Controller采用命名约定:
<form action="@{Application.createUser}"> <input name="name" /> <input name="password" /> <input type="submit" value="Create User" /> </form>
无需其他任何配置,Play!会自动映射form中的name和password参数至createUser方法。
View层Play!使用以Groovy语法写好的html模板中去以render()方法的参数渲染,并将结果回传给客户端。
外界介绍:
Play!虽然使用简单,扩展性却非常强大,篇幅所限所述不能详尽。http://www.playframework.org 是Play!的官方网站,推荐大家到这儿看看。Play!的文档非常详细,教程中有份手把手做一个Blog引擎的教程,相信照着做一下之后一定会让你学会Play! Framework,那时你一定会爱上她的!
貌似正常的开发流程,总要面对项目原型构建的问题,重新发明轮子,或者把以前的家伙式儿再搬出来reuse一下?此时此景,都不是最佳选择。
90%的web业务开源系统都是php版本的,特别是新兴产品的开源实现,java的身影几乎销声匿迹了。可惜j要ava出身的人去阅读php风格的代码简直是一种受虐。这其实说明一个道理,java优美的架构还是很有价值的。但优美+务实的平衡才是最佳选择。
这不,基于java的类RoR风格的full-stack framework的话题又回到我的视线内了,几年前是appfuse,基于此搞过几个小项目,但发现熟悉appfuse本身就已经稍重了。而 appfuse看起来还真只是个toy,demo show +study 为主。
后来国内出现了springside,应该是借鉴了appfuse的思想,更简单,更符合国情一些了。基于springside开发中小项目也没问 题,但国内的开源产品很少能在真正意义上通过多人协作持续变得越来越健壮,版本发布计划总是落单,向下兼容,核心代码等品质还不稳定。
再到今天的play!framework,几个同事也对play!framework很看好,貌似是目前java阵营里最ROR,同时java开发人员又最易上手的框架了。
不得不承认,web开发已经完全是php等脚本语言占优了,就算你开发新一代的企业级产品,你也会发现互联网化+轻量化也是个潮流。就算是IBM team collaboration software – Lotus Quickr ,虽然server端是开发了10多年的老系统。但quickr要做的也是在应用层上基于server做业务扩展。这样,再复杂的系统,在前端应用层上来 看其实也可以轻装上阵了。
很多java开发人员对ROR风格不屑一顾甚至根本不关注,不了解,也许是被sun+ibam下毒太深了吧?其实想想,spring的基于接口 bean管理的xml在很多时候也是写一次,到项目下线也未曾修改过。
java本身也太过强调仅仅java语法层面上的通用,抽象,配置,甚至太OO了,在java诞生的时代,这种做法是很先进的,而当今web应用开 发算是一个特定领域,这种不与时俱进,不考虑web开发实际特点的做法有点杀鸡用牛刀之嫌,必然打不过转为web开发场景设计的新的动态脚本语言。 java毕竟不是专为web开发设计的语言,随着时间的推移,java的身躯越来越笨重,版本升级很慢,期待jdk7加强对动态语言的支 持:JSR292。但这还不够,就像ruby on rails一样。还得有一个rails框架支撑。java官方的脚步实在是。。。只能用非官方的框架了。
在中国,企业级系统至少一大半是比互联网线上产品还小的系统。特别是中国互联网公司的玩得还是硬气功,之内的企业级系统开发很多还是停留在一个人, 两三个人搞定一个系统的层次上。那这样看来,就算有人认为play!framework不一定适合巨复杂的业务系统,那怎么也很适合这样场景的系统开发了 吧。
appfuse更新的越来越慢,是有原因的。因为play!framework这样的新一代类java ROR full-stack framework已经初长成。Grails,JRuby,也占据新一代java 类ROR full-stack framework的市场,但与之相比,play!framework的好处是兼容现有的java代码,语法,更加适合过渡期的需要,当然由于基于 java这种强类型的静态类型安全检查语言,这也势必通过一些hack手法使play!变得很ROR。
周末网上找了些资料,在电脑上play了一下play!framework,的确比appfuse,springside都RoR,当然就更好用。
play!framework 是无状态的mvc框架,搭配memcached,伸缩性应该没啥问题(集群水平扩展应该很好做。)
play!framework 支持jrebel类似的j效果,修改java代码,无须重启,热部署,很方便。再也不用重复edit-》complie-》deploy-》running漫长之路了。
还有,java mvc框架最麻烦,最弱的就属view层的模板语言和js实现了,play!framework 采用的是Gsp (Groovy server page),及jquery框架。模板语法简练很多。
也不要被play这个词蒙蔽,play!framework 还是很DDD的。只有属性和get,set方法的贫血模型不见了,取而代之的是有行为的充血模型,很DDD吧。
play!framework 真的很full-stack,连httpserver都给你提供了,
play!framework 用Python取代Maven来完成创建,运行等与系统的交互。appfuse之所以学习成本比较高,其实都花在maven本身的配置,理解,使用上了。Python应该更适合于系统打交道,更轻便吧。
play!framework 本身支持插件机制。扩展应该也没问题。估计也一大堆可用的组件,这个很重要,http://www.playframework.org /modules:spring ,orm(应该也有Ibatis吧),lucene search,MongoDB,甚至Scala。都很不错。
想在Model层用一下no-sq的话,Siena组件可以做介个(Siena orm+mongodb就可以了吧)
The siena module automatically enable Siena support for your application.
Read more at http://www.sienaproject.com/
我想,支持可插拔组件的框架是衡量一个好框架最重要的指标,一下子事无巨细都塞给你,你肯定消化不良,由繁入减可太难了。好多开源实现都犯同样的问 题。搞得你在相当长的一段时间内很头大,饱尝贪多嚼不烂之苦。play!framework 的默认配置可真清爽,连spring bean管理都不是默认配置,根据业务需求不断做加法,正是我想要的。哈哈。
缺点:
有人声称play!framework不适合多人协作开发,晕,门户网站千万级用户量的系统核心开发人员也不会超过5-8人。更何况,在多人开发的 情况下也是尽量一人负责一块儿的,相反,现在好多开源框架恰恰过多考虑了不符合中国实际的多人协同开发场景,搞得框架分离得过于松散,过于流水线,搞得一 两个人开发项目时,反倒觉得麻烦得要死。所以不适合多人协作开发好像也算不上什么缺点。
ROR COC就是要改变java程序员妄图对一切应用都机械地力求基于接口编程,通用,显式配置才放心的习惯。大部分web应用开发只要做到 webservice restful api层面上的面向接口编程,和通用性基本就足够了。不要忘了restful api本身已经是抽象得巨好的架构了。这样,web开发人员才可能提升开发效率,把时间和精力放到业务逻辑本身,这样才有敏捷开发的可能。想想,如果你的 开发要点和时间都消耗在未雨绸缪,坐在椅子上凭空想象n年后的需求变更,而作所谓的基于接口编程,再加上缓慢的编译,部署,运行,怎么可能适应敏捷开发的 要求呢?反而会导致一个项目虎头蛇尾,前期优雅得要命,后期丑陋得要命,都往action甚至jsp里堆代码了,何苦呢?反不如,一开始就低下你高贵的头 颅,心平气和务实地去ROR了。
总之,简单说,如果play!framework真的兼容了java与php,ruby等动态语言的优势,那就是目前java人员web开发最佳的选择之一。
=====================================================
什么是 Play 框架?
Play是一个开源的现代web框架,用于编写Java和Scala的可扩展Web应用程序。它通过自动重载变化来提高生产力,由于设计的就是一个无状态、无阻塞的架构,所以用Play框架来编写横向扩展Web应用程序是很容易的。
为什么要用它?
我的原因是:
-
开发人员生产力:我已经写了8年的Java,但在过去的几个月里我把更多的时间花在了Python和JavaScript
(Node.js) 上。用动态语言工作时最让我吃惊的,就是用它编写程序的速度是如此之快。Java
EE和Spring框架并不是快速原型和开发的理想选择,但在用Play框架时,你更改一处刷新一下页面,更新会立即出现,而且它支持热重载所有的
Java代码、模板等,可以让你的迭代快很多。 -
天性使然:Play框架是建立在Netty之上的,所以它支持非阻塞I/O,这使得并行远程调用容易了很多,这一点对面向服务的架构中的高性能应用程序是很重要的。
-
支持Java和Scala:Play框架是一个真正的多语种Web框架,开发者可以在项目中同时使用Java和Scala。
-
一流的REST JSON支持:它很容易编写基于REST的应用。对HTTP路由有很好的支持,HTTP路由会将HTTP请求转化为具体动作;JSON编组/解组API是目前的核心API,所以没有必要加一个库来做到这一点。
应用类型案例
今天的介绍中,将开发一个社交书签应用程序,它允许用户发布和共享链接。你可以在这里查看正在运行的该程序,因为这个和第22天的应用是一样的,所以请参阅之以便更好地了解这个案例。
开发Play应用
请参阅文档以了解如何安装Play框架,开始应用程序的开发吧。
$ 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 > 2OK, application getbookmarks is created. Have fun!
如上键入命令后,该框架会问几个问题。首先它要求有应用程序的名称,然后问是否要创建一个Scala或Java应用程序。默认情况下,它会使用文件夹名称
作为应用程序的名称。
上面的命令将创建一个新的目录getbookmarks
并生成以下文件和目录:
-
app 目录包含如控制器 (controller) 、视图 (view) 和模型 (model)
的应用程序特定代码。控制器包中有响应URL路由的Java代码,视图目录包含服务器端模板,模型目录包含应用程序的域模型。在此应用中,域
(domain) 是一个Story类。 -
conf 目录包含应用程序配置和路由定义文件。
-
project 目录包含构建脚本,构建系统是基于SBT的。
-
public 包含了如CSS、JavaScript和img目录等的公共资源。
-
test 目录包含应用测试。
通过如下命令发布play控制台,运行Play编写的默认程序。
$ 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...)
现在可以在 http://localhost:9000
里运行该应用了。
创建Story域类
该应用程序只有一个域类 (domain class),叫做story,创建一个新的包模型和Java类。
package models;import play.db.ebean.Model;import javax.persistence.Entity;import javax.persistence.Id;import java.util.Date;@Entitypublic 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}
上述代码定义了一个简单的JPA实体,并使用 @Entity
和 @Id
JPA注解,Play用它自己的一个被称作Ebean的ORM层,而且每一个实体类必须扩展基本模型类。
Ebean默认禁用,启用它需要打开application.conf
并取消注释以下行。
ebean.default="models.*"
启用数据库
启动应用程序的数据库,Play框架提供了内置的H2数据库的支持。要启用它,打开application.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 pageGET / 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 pathGET /assets/*file controllers.Assets.at(path="/public", file)
上述代码表示:
-
当用户发出一个GET请求到应用程序的
“/”URL
,index.html
将被渲染。 -
当用户发出一个GET请求到
'/ api/v1/stories'
,将得到JSON格式的所有story。 -
当用户发出POST请求到
'/ api/v1/stories'
,一个新的story将被创建。 -
当用户GET请求
'/ api/v1/stories/123'
,id为123的story会被渲染。
创建Story控制器
在控制器包里创建一个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; } } }
上述代码会操作:
-
它定义
allStories()
方法,该方法会找到数据库中所有的story。它是使用Model.Finder API来做到这一点的,然后把story列表转换成JSON格式并返回结果,返回HTTP状态代码200(即确定)。 -
submitStory()
方法首先会从JSON读取URL和全名的字段,然后发送GET请求到'http://gooseextractor-t20.rhcloud.com/api/v1/extract?url'
,这样就会找出标题、摘要以及已经给定url的主要image。创建一个使用所有信息的story并保存在数据库中,返回HTTP状态代码201(即创建)。 -
getStory()
方法为给定的storyId获取story,把这个story转换成JSON格式并返回响应。
可以从我的github仓库下载AngularJS前端,用其中一个库更换公共目录。
现在可以访问http://localhost:9000/
看结果了。
转载请注明:学时网 » play framework介绍总结