zoukankan      html  css  js  c++  java
  • 再说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.       
    4.     @Required  
    5.     public String name;  
    6.     public String address;  
    7.     public String city;  
    8.       
    9.     …..省略部分字段  
    10.   
    11.     @Column(precision=6, scale=2)  
    12.     public BigDecimal price;  
    13.   
    14.     public String toString() {  
    15.         return "Hotel(" + name + "," + address + "," + city + "," + zip + ")";  
    16.     }  
    17.       
    18.    }  

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

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


       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.   
    14.  public static void list(String search, Integer size, Integer page) {  
    15.      List<Hotel> hotels = null;  
    16.      page = page != null ? page : 1;  
    17.      if(search.trim().length() == 0) {  
    18.          //分页的代码是不是很简单?链式调用更加方便  
    19.          hotels = Hotel.all().fetch(page, size);  
    20.      } else {  
    21.          search = search.toLowerCase();  
    22.          hotels = Hotel.find("lower(name) like ? OR lower(city) like ?", "%"+search+"%", "%"+search+"%").fetch(page, size);  
    23.      }  
    24.        
    25.      render(hotels, search, size, page);  
    26.  }  
    27.    
    28.  public static void book(Long id) {  
    29.      Hotel hotel = Hotel.findById(id);  
    30.      render(hotel);  
    31.  }  
    32.    
    33.  public static void confirmBooking(Long id, Booking booking) {  
    34.      Hotel hotel = Hotel.findById(id);  
    35.      booking.hotel = hotel;  
    36.      booking.user = connected();  
    37.      validation.valid(booking);  
    38.        
    39.      // Errors or revise  
    40.      if(validation.hasErrors() || params.get("revise") != null) {  
    41.          render("@book", hotel, booking);  
    42.      }  
    43.        
    44.      // Confirm  
    45.      if(params.get("confirm") != null) {  
    46.          booking.save();  
    47.          flash.success("Thank you, %s, your confimation number for %s is %s", connected().name, hotel.name, booking.id);  
    48.          index();  
    49.      }  
    50.        
    51.      // Display booking  
    52.      render(hotel, booking);  
    53.  }  
    54.        
    55.  public static void saveSettings(String password, String verifyPassword) {  
    56.      User connected = connected();  
    57.      connected.password = password;  
    58.      validation.valid(connected);  
    59.      validation.required(verifyPassword);  
    60.      validation.equals(verifyPassword, password).message("Your password doesn't match");  
    61.      if(validation.hasErrors()) {  
    62.          render("@settings", connected, verifyPassword);  
    63.      }  
    64.      connected.save();  
    65.      flash.success("Password updated");  
    66.      index();  
    67.  }  
    68.    


       上面的代码中展示了 
    1.@before 这个注解基本就是个拦截器的意思,所有访问这个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.     
    2.   public static void save(Picture picture,File pic){  
    3.       
    4.         File uploadFile=new    File(Play.applicationPath.getAbsoluteFile()+”/public/uploads”);   
    5.     play.libs.Files.copy(pic,uploadFile);  
    6.     picture.url =path;  
    7.     picture.save();  
    8.     QZ_Admin.pictures();  
    9. }  


      其它的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("from@163.com", "to@163.com", "",result,"text/html")    

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

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


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






  • 相关阅读:
    Asp.Net Core 轻松学-被低估的过滤器
    Asp.Net Core 轻松学-利用文件监视进行快速测试开发
    Asp.Net Core 轻松学-利用xUnit进行主机级别的网络集成测试
    Asp.Net Core 轻松学-HttpClient的演进和避坑
    Asp.Net Core 轻松学-基于微服务的后台任务调度管理器
    Asp.Net Core 轻松学-一行代码搞定文件上传
    .NET Core 2.2 新增部分功能使用尝鲜
    Asp.NetCore轻松学-业务重点-实现一个简单的手机号码验证
    Asp.Net Core 轻松学-实现跨平台的自定义Json数据包
    Asp.Net Core 轻松学-利用 Swagger 自动生成接口文档
  • 原文地址:https://www.cnblogs.com/zhiji6/p/4444847.html
Copyright © 2011-2022 走看看