再说Play!framework http://hsfgo.iteye.com/blog/806974

这篇帖子的内容我本来想发到 http://www.iteye.com/topic/806660这里的主贴里去的,想挽回被隐藏的命运,但我写完本贴的内容,却发现为时已晚。好吧,我承认,上一个贴的标题容易引发口水,这次我们实事求是,从代码出发,通过一个小例子较完整的介绍play!framework的开发过程:

就拿play!framwork自带的房间预订(booking)的例子吧:

1、 下载play  解压,配置环境变量 
2、 打开命令行:转到合适的目录,输入Play new booking   这样,项目即生成完毕。 
3、     进入项目目录中,执行play eclipsify  或者play netbeansify  这样即可将生成的项目导入到eclipse或者netbeans中。

打开项目目录我们可以看到:

 
解释下各个目录:

  • app 包含所有的model,controller以及view(模板)。
  • conf下是一个application.conf 配置文件。
  • lib是Play依赖的第三方jar。
  • logs是日志
  • public下包含你引用的js,css以及,images等。
  • test下所有的测试文件在此。

这样的一个目录显然与传统的JEE目录完全不一样,事实上,它已经摒弃了servlet,jsp那些东西,而完全自己实现了HTTP,您会问,那它是不是无法正常运行于标准的servlet容器中,请不要担心,我们在开发完成后可以使用命令play war –odir  这个命令生成可以正常运行于servlet容器中的项目目录。 
  Play分为开发模式和生产模式两种,而切换的配置在application.conf中: 
  Application.mode=dev 生产模式请改为:prod 
  主要区别在于开发模式中您无需重启server,每次请求都会查看是否有文件发生改变,改变即编译,这对于传统Java EE开发人员无疑是相当敏捷的。而这种方式同样会导致性能下降,所以生产模式中就不会这样了,而是采用预编译机制。 
  
  下面开始coding: 
  按照OO的开发模式 首先编写模型层: 
  在app包下新建类Hotel.java 继承Model ,如下:

Java代码  

  1. @Entity
  2. public class Hotel extends Model {
  3. @Required
  4. public String name;
  5. public String address;
  6. public String city;
  7. …..省略部分字段
  8. @Column(precision=6, scale=2)
  9. public BigDecimal price;
  10. public String toString() {
  11. return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";
  12. }
  13. }

其中继承Model基类实现了一个富血的Domain Model(这不是比传统的PO更加OO ?),完全基于JPA,上手非常简单

同样Booking类:

Java代码  

  1. @Entity
  2. public class Booking extends Model {
  3. @Required
  4. @ManyToOne
  5. public User user;
  6. @Required
  7. @ManyToOne
  8. public Hotel hotel;
  9. @Required
  10. @Temporal(TemporalType.DATE)
  11. public Date checkinDate;
  12. @Required
  13. @Temporal(TemporalType.DATE)
  14. public Date checkoutDate;
  15. @Required(message="Credit card number is required")
  16. @Match(value="^\\d{16}$", message="Credit card number must be numeric and 16 digits long")
  17. public String creditCard;
  18. @Required(message="Credit card name is required")
  19. public String creditCardName;
  20. public int creditCardExpiryMonth;
  21. public int creditCardExpiryYear;
  22. public boolean smoking;
  23. public int beds;
  24. public Booking(Hotel hotel, User user) {
  25. this.hotel = hotel;
  26. this.user = user;
  27. }
  28. public BigDecimal getTotal() {
  29. return hotel.price.multiply( new BigDecimal( getNights() ) );
  30. }
  31. public int getNights() {
  32. return (int) ( checkoutDate.getTime() - checkinDate.getTime() ) / 1000 / 60 / 60 / 24;
  33. }
  34. public String getDescription() {
  35. DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
  36. return hotel==null ? null : hotel.name +
  37. ", " + df.format( checkinDate ) +
  38. " to " + df.format( checkoutDate );
  39. }
  40. public String toString() {
  41. return "Booking(" + user + ","+ hotel + ")";
  42. }
  43. }

User类 类似,不再给出。

编写Controller  在controller包中编写类:Hotels  继承Controller(这儿Application继承Controller)

Java代码  

  1. public class Hotels extends Application {
  2. @Before//拦截器
  3. static void checkUser() {
  4. if(connected() == null) {
  5. flash.error("Please log in first");
  6. Application.index();
  7. }
  8. }
  9. public static void index() {
  10. List<Booking> bookings = Booking.find("byUser", connected()).fetch();//这句是不是更加面向对象?
  11. render(bookings);
  12. }
  13. public static void list(String search, Integer size, Integer page) {
  14. List<Hotel> hotels = null;
  15. page = page != null ? page : 1;
  16. if(search.trim().length() == 0) {
  17. //分页的代码是不是很简单?链式调用更加方便
  18. hotels = Hotel.all().fetch(page, size);
  19. } else {
  20. search = search.toLowerCase();
  21. hotels = Hotel.find("lower(name) like ? OR lower(city) like ?", "%"+search+"%", "%"+search+"%").fetch(page, size);
  22. }
  23. render(hotels, search, size, page);
  24. }
  25. public static void book(Long id) {
  26. Hotel hotel = Hotel.findById(id);
  27. render(hotel);
  28. }
  29. public static void confirmBooking(Long id, Booking booking) {
  30. Hotel hotel = Hotel.findById(id);
  31. booking.hotel = hotel;
  32. booking.user = connected();
  33. validation.valid(booking);
  34. // Errors or revise
  35. if(validation.hasErrors() || params.get("revise") != null) {
  36. render("@book", hotel, booking);
  37. }
  38. // Confirm
  39. if(params.get("confirm") != null) {
  40. booking.save();
  41. flash.success("Thank you, %s, your confimation number for %s is %s", connected().name, hotel.name, booking.id);
  42. index();
  43. }
  44. // Display booking
  45. render(hotel, booking);
  46. }
  47. public static void saveSettings(String password, String verifyPassword) {
  48. User connected = connected();
  49. connected.password = password;
  50. validation.valid(connected);
  51. validation.required(verifyPassword);
  52. validation.equals(verifyPassword, password).message("Your password doesn‘t match");
  53. if(validation.hasErrors()) {
  54. render("@settings", connected, verifyPassword);
  55. }
  56. connected.save();
  57. flash.success("Password updated");
  58. index();
  59. }

上面的代码中展示了 
[email protected] 这个注解基本就是个拦截器的意思,所有访问这个Controler方法的请求都会先执行@before方法。 
2、controller中的几个作用域:

  • 1、session这儿的session只支持您放里面放String类型,而不是和传统JEE中任何对象都可以放到session中。这儿的session和rails的类似。
  • 2、flash 跨请求的存储对象
  • 3、params  基本相当于request.getParameters();
  • 4、renderArgs  渲染到模板的数据,上面代码中您看到的render里面的就是放到了这个renderArgs里面了。还有个validation存放验证数据。

基类Controller里定义了很多好用的方法:如果您想使用ajax返回JSON,则使用renderJSON()  play使用的json序列化工具是gson.jar,您想返回一个文件流,使用renderBinary(File f,String name)方法。 
  上面没有展示文件上传的代码:我再贴一个文件上传的代码:

Java代码  

  1. public static void save(Picture picture,File pic){
  2. File uploadFile=new    File(Play.applicationPath.getAbsoluteFile()+”/public/uploads”);
  3. play.libs.Files.copy(pic,uploadFile);
  4. picture.url =path;
  5. picture.save();
  6. QZ_Admin.pictures();
  7. }

其它的Controller不再给出

这儿会有同学问,我没有配置和URL映射规则啊。事实上,play借鉴rails默认大于配置的思想,默认的映射规则是/Controller/method?params  这种。当然您也可以在配置文件routes中重新设定您所需要的映射规则。同时模板的位置默认也和Controller的名字有很大关系,比如这人我们Controller的名字叫Hotels  方法名是Index  那如果您不指定渲染模板的话默认play会去views 下面的Hotels文件夹下找index.html模板。这种约定是不是限制了很多东西?会不会对开发造成一些影响,我个人认为是有的,由于和Controller,method的名字关系密切,这需要你良好的规划,以保证你的项目目录的合理,以及URL的优雅。

最后是编写模板: 
   在views  下面建立文件夹 Hotels  新建文件index.html

Java代码  

  1. #{extends ‘main.html‘ /}///在views文件夹下面编写main.html一般为网站所有页面的公共部分,比如header和footer
  2. #{set title:‘Search‘ /}//为每一个页面设置title  在Main.html有变量title
  3. <table>
  4. <thead>
  5. <tr>
  6. <th>Name</th>
  7. <th>Address</th>
  8. <th>City, State</th>
  9. <th>Check in</th>
  10. <th>Check out</th>
  11. <th>Confirmation number</th>
  12. <th>Action</th>
  13. </tr>
  14. </thead>
  15. <tbody>
  16. #{list bookings, as:‘booking‘}  //遍历
  17. <tr>
  18. <td>${booking.hotel.name}</td>
  19. <td>${booking.hotel.address}</td>
  20. <td>${booking.hotel.city},${booking.hotel.state}, ${booking.hotel.country}</td>
  21. <td>${booking.checkinDate.format(‘yyyy-MM-dd‘)}</td>
  22. <td>${booking.checkoutDate.format(‘yyyy-MM-dd‘)}</td>
  23. <td>${booking.id}</td>
  24. <td>
  25. #{a @cancelBooking(booking.id)}Cancel#{/a}
  26. </td>
  27. </tr>
  28. #{/list}
  29. </tbody>
  30. </table>

这样,一个简单的模板页面就编写完成了。Play的模板相较于jsp或者JSTL以及struts2标签啥的都更加简单,也没有freemarker 空指针异常(可能有童鞋喜欢这个)这些问题。具体其它的用法可以参看play的帮助文档。

以上基本上就把play的大体用法说完了,现在我再写下play其它让人心动的地方: 
1、 缓存支持:

Java代码  

  1. 2、  public static void showProduct(String id) {
  2. 3、      Product product = Cache.get("product_" + id, Product.class);
  3. 4、      if(product == null) {
  4. 5、          product = Product.findById(id);
  5. 6、          Cache.set("product_" + id, product, "30mn");
  6. 7、      }
  7. 8、      render(product);
  8. 9、  }

而您可以使用EhCache或者Memcached作为缓存的实现。使用起来非常方便

2、 JOB支持:

Java代码  

  1. 3、  @Every("1h")
  2. 4、  public class Bootstrap extends Job {
  3. 5、
  4. 6、      public void doJob() {
  5. 7、          List<User> newUsers = User.find("newAccount = true").fetch();
  6. 8、          for(User user : newUsers) {
  7. 9、              Notifier.sayWelcome(user);
  8. 10、         }
  9. 11、     }
  10. 12、
  11. 13、 }

这段代码即会每1小时运行一次。

3、 Email支持:

Java代码  

  1. 4、  Template t =TemplateLoader.load("UserCenter/mailTemplate.html");//邮件模板
  2. 5、  Scope.RenderArgs templateBinding = Scope.RenderArgs.current();
  3. 6、  templateBinding.put("url","http;//url"));
  4. 7、  String result =t.render(templateBinding.data);
  5. 8、  Mail.send("[email protected]", "[email protected]", "",result,"text/html")

以上即是使用模板发送邮件的例子。当然您需要在application.conf指定发邮件的一些参数

4、非常多的好用的module:比如支持lucene的search-module ,MongoDB module,GAE module,Excel Module,GWT Module,PDF Module等等。

以上大体介绍了play!framework的开发示例以及一些基本特点。大家可以讨论下你对Play!framework看法,但请勿鄙视一把走人,或者发表带有人身攻击的言论,谢谢!

时间: 2024-11-05 13:35:41

再说Play!framework http://hsfgo.iteye.com/blog/806974的相关文章

Mybatis入门实例(三)——使用MyBatis Generator生成DAO(转载http://qiuqiu0034.iteye.com/blog/1163026)

接上回 http://qiuqiu0034.iteye.com/blog/1162952 虽然MyBatis很方便,但是想要手写全部的mapper还是很累人的,好在MyBatis官方推出了自动化工具,可以根据数据库和定义好的配置直接生成DAO层及以下的全部代码,非常方便. 需要注意的是,虽然自动化工具需要一个配置文件,但是MyBatis的配置文件仍然不能少,自动化工具的配置文件用于对生成的代码的选项进行配置,MyBatis的配置文件才是运行时的主要配置文件. 这个工具叫做MyBatis_Gene

http://stamen.iteye.com/blog/1441794Spring @Trancation注解

Spring可以通过注解@Transactional来为业务逻辑层的方法(调用DAO完成持久化动作)添加事务能力. @Tranactional注解分析 作用域:Transactional作用于类上表示类上所有的方法使用指定的事务管理策略,作用于方法表示该方法使用指定的事务管理策略:如果方法和类同时定义了@Tranactional注解,那么方法上定义的@Tranactional的属性覆盖类上定义的@Tranactional的属性 value: 用于匹配Spring中定义的tranactionMan

学习windows内核书籍推荐 ----------转自http://tieshow.iteye.com/blog/1565926

虽然,多年java,正在java,看样子还得继续java.(IT小城,还是整java随意点)应用程序 运行于操作系统之上,  晓操作系统,方更晓应用程序.主看windows,因为可玩性高,闭源才有意思.(莫忘2008年,微软盗版黑屏事件)书籍推荐(全中文 ),按顺序======  windows应用程序  =============C&C++, 略 (懒~,就那几本,程序员都知道)<windows程序设计> (第5版.珍藏版), 非珍藏版的翻译垃圾,莫看<WINDOWS.核心编程

java web开发总结(五):如何进行系统优化的思考 (http://hillside.iteye.com/blog/580639)

前两天跟同事讨论,说到高并发系统如何做优化,提到这个问题,他说他有些茫然,有点不知道该如何下手. 我想了想这几年做的各种系统优化工作,正好也简单总结一下,总结起来就是:一个核心,N种手段. 一个核心就是:多.快.准. N种手段就要围绕上面的核心做的各种处理. 上面这个核心字多点说也就是:更多用户访问.更短响应时间.数据正确性. 优化的过程,我的想法就是先顺藤摸瓜,沿着一个请求发生的路径一路看过去,测量一下每个点上消耗的时间,会发现很多消耗时间多的点,都是值得你去优化的地方.然后再考虑在每个点上发

spring web flow 2.0入门(转:http://luanxiyuan.iteye.com/blog/2282126)

Spring Web Flow 2.0 入门详解 博客分类: spring 目录: 参考文献 购物车用例 什么情况下可以使用 Spring Web Flow? 配置 Spring Web MVC 配置 Spring Web Flow 2.0 的基础 在购物车示例应用中配置 Spring Web Flow 用 Unified EL 实现业务逻辑 用 subflow 实现添加商品到购物车功能 global transition 简介 1.参考文献 参考1:http://www.ibm.com/dev

如何防止SQL注入 http://zhangzhaoaaa.iteye.com/blog/1975932

如何防止SQL注入 博客分类: 技术转载数据库 转自:http://021.net/vpsfaq/152.html -----解决方案-------------------------------------------------------- 过滤URL中的一些特殊字符,动态SQL语句使用PrepareStatement.. ------解决方案-------------------------------------------------------- 注入的方式就是在查询条件里加入SQ

内部类修改外部类属性(转:http://demojava.iteye.com/blog/1075571)

public class Test {  public String demo="qw4rqwer";  public void changValue() {  this.demo=" hello word"; } class DemoOuterclass {   public  DemoOuterclass()   {    changValue();   } } public static void main(String[] args) {  Test t=n

《C和指针(Pointer on c)》 学习笔记(转自:http://dsqiu.iteye.com/blog/1687944)

首先本文是对参考中三个连接的博客进行的整理,非常感谢三位博主的努力,每次都感叹网友的力量实在太强大了…… 第一章 快速上手 1.  在C语言中用/*和*/来注释掉这段代码,这个实际上并不是十分的安全,要从逻辑上删除一段C代码,最好的办法是使用#if指令: #if 0 Statement #endif 2.  其他语言中,无返回值的函数称为过程(procedure). 3.  数组做参数的时候是以引用(reference)的方式传递的,即地址传递.而标量和常量都是传值调用,被调用的函数无法修改调用

Endian.BIG_ENDIAN和Endian.LITTLE_ENDIAN(http://smartblack.iteye.com/blog/1129097)

Endian.BIG_ENDIAN和Endian.LITTLE_ENDIAN 在ByteArray和Socket中,能看到一个属性endain. endian : String 更改或读取数据的字节顺序:Endian.BIG_ENDIAN 或 Endian.LITTLE_ENDIAN. 谈到字节序的问题,必然牵涉到两大CPU派系.那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU.PowerPC系列采用big endian方式存储数据,而x86系列则采用little