Freewind @ Thoughtworks scala java javascript dart 工具 编程实践 月结 math python english [comments admin] [feed]

(2012-07-28) Play Framework(1/2)入门引导

广告: 云梯:翻墙vpn (省10元) 土行孙:科研用户翻墙http proxy (有优惠)

为了方便群中的Play初学者们,写了一篇入门引导,以帮助初学者尽快了解Play。本文之前发在另一个网站,因为觉得有些不便,还是转到博客上。

欢迎来到play的世界,在这里你将体验到与传统SSH开发网站不一样的感受。我将把我学习play的感受与经验分享给大家,希望能对大家(特别是初学者)有所帮助。

Play是一个非常有创造力、让人眼前一亮的Java web开发框架。它把网站开发中常见的繁琐的任务,以各种突破常规的方式简化,“不要重复你自己”,让人在开发过程中有一种享受的感觉。使用Play实现功能,有时候简单地让人难以相信(对于长期使用SSH的人来说尤其明显)。

在play的google group中,曾经有人说,当地要举办一次编程大赛,他将使用play参赛。每个参赛队可使用任何语言和框架,在6小时内实现指定的功能。结果他们胜利了,打败了Ruby on rails队。使用Play开发网站,在不使用各种第三方插件的情况下(Rails强项),开发效率与Rails在同一级别。

这里将介绍一些最让我印象深刻的特性:

  1. 修改不用重启:这是play最为吸引人的一点。修改任何java或模板代码,都不需要重启server,直接在浏览器中刷新即可看到最新效果。这曾是多少Javaer梦想的功能。结合livereload等工具,可以实现光标不离开编辑器就能实时看到最终页面效果的功能。

  2. 无状态:Play是无状态的,不保存与客户端相关的数据,比如它的session是以加密方式保存在cookie中的。这意味着play程序拥有良好的扩展性,比如当访问压力过大时,可以简单地多启动几个play实例即可(使用不同端口+反向代理)。由传统的Servlet程序则需要考虑session同步等问题。

  3. 代码增强:代码增强是Play的重点功能,也是它最富于创新力的地方。在play导入我们所写的java源代码时,会使用javassist这个工具,根据内置或第三方的插件的要求修改字节码,实现一些原本无法实现的功能。比如充血模型,生成getter/setter,根据参数名自动从request中取参数,根据参数名自动向template中设置参数等。我们可以省去很多不必要的代码和为因Java语言限制而存在的设计模式,写出简洁清晰的代码。由于“代码增强”的存在,使得play与众不同。

  4. 模板中方法调用与url的自动转换:在play的模板中,需要与后台交互(比如跳转页面)时很少写url,因为我们可以直接调用相应的Action和参数,由Play根据routes文件中的定义生成相应的url。当url更改时,直接修改routes文件即可,不需要动模板文件。当我们写错(如方法名)时,play会自动提示编译错误。该功能让我们重构url时非常轻松。

  5. 对restful api的良好支持:在routes文件中,通过简单的规则就可以定义出restful api的接口。通过在server端生成jsRoutes(包含了restful api的信息),在浏览器端可以方便地使用ajax与后台交互。

  6. 内置基于netty的web服务器:play内嵌了基于netty的高性能服务器,只需要一条命令即可运行起来,简单而强劲,推荐使用。如果因为服务器限制而必须使用传统的servlet服务器的话,可将play程序打包为war部署。

Play的版本

Play当前的版本有点乱,不同版本之间差别还相当大,这常常让初学者一头雾水。Play目前可分为三个版本,它们之间没有谁比谁好,只有谁比谁适合。大家可根据自己的实际情况来选择合适的版本来学习。

首先是Play的成名之作:Play1

Play1.x是成名之作。它是一个Java开发框架,使用python作为构建系统。内置了Jpa(Hibernate)和基于groovy的模板系统。它大量使用了代码增强,提供了大量魔术般的功能,以提高程序员的开发效率和代码的简洁性。它编译速度快,代码简洁,让人开发时非常享受。Play1当前最新版本为1.2.5,它是三者间最为成熟的一个版本,拥有大量成熟的第三方模块供使用。这里值得一提的是群友green开发了大量基于play1的优秀插件,如rythm/play-morphia等,进一步提高了play的性能及开发效率。虽然play1目前已处于维护状态,但如果你需要在近期开发传统的信息管理类网站,它仍然是最佳选择。

关于Play2

几个月前,Play1的开发者加入了typesafe.com,并且play作为其官方web框架。typesafe是一个力推scala的公司,所以新推出的play2,实际上是将scala作为第一开发语言的。比如play2中使用的新构建工具sbt,是scala的标准构建工具;play2中很多底层代码都是用scala写的,由Java来调用;play2的默认模板引擎,也是基于scala的。在play2中,处处可见scala的痕迹。但play2也同时提供了java api,以吸引广大的Java开发者。由于Scala与Java在语言特性与风格上的巨大差别,play2-scala与play2-java,在很多地方都不同。

play2相对于play1,不是一次简单的版本升级,而是几乎重写了全部代码。Play2与Play1在很多方面都不同,不能通用。也许它本来就不该叫play2的,它与play1的区别,如同struts2与struts1的区别(struts2其实是由另一个叫webwork的框架改名而来)。Play开发团队从商业利益上的考虑作出的决定(以及某种程度的不负责任),曾经在play社区中引起激烈争论,大批play1用户表达了自己的不满和对play2的失望。

Play2在开发风格上与Play1有所不同。如同typesafe公司名称所示,Play2在最大程度的利用编译器的检查,以求更加稳定可靠。在Play1中一些追求简洁的魔术代码,在Play2中取消了,相反要使用一些略显繁琐的代码来实现相同功能。同时因为scala相当慢的编译速度,让热修改后生效的时间大大延长,有时候难以忍受(在play1中1秒以内,play2中要5秒以上)。所以很多从play1转向play2的用户都非常不适应,拒绝转向play2。

由于Play2的仓促推出,当前的2.0.2版不论在功能上还是稳定性都存在相当多的问题,插件生态系统也没有成熟(Play1的插件不能在Play2上使用)。所以当前直接在生产系统中使用它还是有一定风险的,最好再等几个月。

不论如何,Play2是官方支持,目前所有的开发活动都基于它,它是未来的趋势。它重新组织的代码结构与API,相比1来说,也要精致很多。相信现在存在的各种问题在未来会慢慢解决,只是需要一段时间。

Play2的卖点是并发,因为它底层使用了scala中的akka库,对于开发实时网站程序比较有优势。但对于传统的信息系统类网站,也许Play2很难达到Play1的程度。个人认为,对于以开发信息系统为主的团队,使用Play1在各方面来讲,都会是更好的选择。

对于认为新版一定好于旧版,或敢于尝鲜而选择了Play2的朋友,马上要面临一个新的问题:

是用Play2-Scala还是Play2-Java呢?

个人认为,这个选择还是比较简单的。如果你是Scala程序员,来寻找Scala下的web框架,就选Play2-Scala。它比lift简单易学,容易上手。如果你是Java程序员,或者主要以Java项目为主,请选择Play2-Java。

虽然Scala在宣传上总是以“更好的Java“来作为卖点,但它实际上是一门与Java相差非常大的语言。在语言风格上,Scala融合面向对象与函数式,强调数据的不变性,这都与Java不同。在难度上,Scala要比Java难很多,函数式编程和类型系统会让很多Java程序员止步,通常在六七年的编程经验后再学习Scala是比较靠谱的选择。

另外,虽然两者都是JVM上的语言,但它们之间是有缝的,调用对方的库经常会遇到各种各样的问题。比如你想在Scala上使用java的jpa/ebean/morphia等,会非常麻烦。但scala自己又没有一个足够成熟好用的orm,这可能是阻止你使用Play2-Scala的一个重要原因。

所以,还是如前面所说,Scala程序员选Play2-Scala,Java程序员选Play2-Java。

Play2-Scala

Play2-Scala相对于Scala上的另一个成熟web框架lift来说,优势在于简单易学、有typesafe官方支持,前途光明。同时对于以restful api作为主要目的的程序来说,Play2提供的routes相当好用。劣势在于不如lift成熟,生态系统不如lift。在某些时候,Play2的MVC不如Lift的View-First好用。

Play2使用了sbt,底层是akka,模板层基于scala,orm是Play自己开发的一个叫anorm的持久层。anorm的特点抛弃orm,直接使用jdbc,使用预定义的parser把结果集转为对象。Anorm初看起来比较吸引人,但在实际使用过程中,异常繁琐。对数据库字段的一次修改,往往要牵扯到几个地方,动不动就出错,同时再加上scala奇慢无比的编译速度,极易让人心情烦躁。所以也有人尝试在play2中使用squerl,好在很容易集成。当然还是让我们期待typesafe正在开发的新orm:slick。

对于Scala程序员来说,Play2-Scala是一个比较好的选择,不妨一试。

注意,如果你使用Play2-Scala,想使用Play2中提供的JPA/Ebean时请小心。由于Play2在代码增强时,只增强Java代码,这将导致从Scala中调用它们时出现问题,所以最好选择scala中的orm。

Play2-Java

对于Java程序员来说,还是用Play2-Java比较顺手。虽然相比Play1要繁琐一些,但对于SSH等,还是要简洁很多。Play2中Java的api与Scala的api不在同一个包下,要注意不要引用错了。

对于controller和action,Play2与play1的结构基本相同,依然采用静态方法,但需要返回一个Result。注意的是,Action中的参数,只能匹配在routes中的url定义中出现的参数,而不像play1那样,还能匹配任意query参数和post参数。

Orm方面,同时提供了JPA(hibernate)和Ebean。我个人比较推荐Ebean,相对于hibernate,它的api更加简单,不易出错,因为它是用jdbc的思路。在官方下载包里有一份100多页的pdf文档,看完就差不多了,用起来麻烦绝对比hibernate少太多。我在Play1中就通过第三方的插件使用Ebean,效果很好。也许只有一种情况不能用它,因为它不支持sql server。

Play2基于scala的模板层,对于Java开发者来说是不太方便的。两点原因:

  1. Scala的语法与Java不同,虽然在模板中已经尽量简化了,但难免还是会遇到一些不好下手的问题。2. 模板将被转化为Scala代码进行编译。如前文所说,Play2对于Scala代码不会进行增强,所以在模板中调用Java类可能会有问题。比如直接调用@user.articles,它不会像在Java中那样被替换为user.getArticles,而是直接调用user.articles字段。由于代码增强通常是在getter/setter上进行的,所以这样可能拿不到数据。如果想解决此类lazy loading的问题,我们必须在model中使用传统的javabean方式显式声明getter/setter,然后在模板层中调用@user.getArticles,相当繁琐。

所以在Play2官方推出基于java的模板前,我推荐使用两个第三方模板插件。一是faster groovy template,二是japid,它们都已经支持Play2,且不会有上述问题(Japid待测)。

另外,对于不喜欢Java语法但又不想用scala的同学,可尝试xtend。xtend代码要比Java舒服一些,而且直接生成java源代码,不存在编译等问题。

Play存在的问题

虽然Play在很多方面给我们带来的方便,但难免也会有一些问题需要注意,这里简单提一下。

Play1

Play1主要存在的问题有三个:

  1. 与某些第三方库结合使用时,必须写插件。比如morphia, ebean等。因为Play1需要对我们的Java源代码进行增强,而这些库也需要对字节码进行增强。如果不写插件处理classloader及字节码,会出现无法运行的情况。

  2. 使用javassist修改字节码比较繁琐易错。当Play用到一定程度,难免会需要写自己的插件,对字节码进行增强以实现特殊功能。但使用Javassist来修改字节码,还是一件比较容易出错的工作,经常要花大量的时间在调试上。特别写的插件需要先打包,才能在其它项目中使用,看到效果,非常费时。(如果有简洁方法请告诉我)

  3. 停止开发,只维护。官方现在只开发Play2,对于Play1不再开发新功能,只维护。如果想要增加新功能,只能靠自己或第三方插件。

Play2

Play2主要有以下几个问题:

  1. 目前不够成熟。不论是框架本身还是插件系统,都不太成熟。如果在大型项目中使用,必须自己实现很多功能,以及整合很多第三方库。

  2. 使用sbt作为构建系统。sbt用起来也相当繁琐,要花不少时间才能熟练使用。而且,如果想开发自己的构建插件,还需要学习scala和sbt那些变态的api。这对于Java程序员来说,难度实在太大。

  3. Play2-scala缺少一个好用的orm,Play2-java缺少一个好用的模板层

  4. Play2在某些方面与Play1相比显得繁琐,让用惯Play1的人不易适应。

  5. Scala缓慢的编译速度,常常让人难以忍受,要求用户的电脑配置比较高。

comments powered by Disqus