植物表达载体构建:玩转 Java Web 应用开发:Play 框架

来源:百度文库 编辑:中财网 时间:2024/04/28 18:24:06

玩转 Java Web 应用开发:Play 框架

使用 Play 框架快速开发 Web 应用

成 富, 软件工程师, IBM 中国软件开发中心成富任职于 IBM 中国软件开发中心,目前在 Lotus 部门从事 IBM Mashup Center 的开发工作。他毕业于北京大学信息科学技术学院,获得计算机软件与理论专业硕士学位。

 

简介: 虽然目前有很多种 Java Web 应用开发框架,但 Play 框架是一种新兴的框架,可以帮助开发人员高效的构建Web 应用。本文详细介绍了 Play 框架的使用,包括模型层、控制层、视图层和 HTTP路由等内容。通过本文的介绍,开发人员可以了解到如何用 Play 框架来快速开发 Web 应用。

标记本文!

发布日期: 2010 年 11 月 16 日
级别: 中级
访问情况 3194 次浏览
建议: 3 (查看或添加评论)

平均分 (共 23 个评分 )

概述

Play 框架是一个完整的 Web 应用开发框架,覆盖了 Web 应用开发的各个方面。Play 框架在设计的时候借鉴了流行的Ruby on Rails 和 Grails 等框架,又有自己独有的优势。使用 Play 框架可以方便和高效的开发出 Java Web应用。通过 Play 框架提供的命令行工具,可以快速的创建出一个 Web 应用的基本骨架。它的 Java代码动态编译机制,使得修改代码之后,不需要重启服务器就可以直接看到修改之后的结果,调试起来非常方便。它使用 JPA规范来完成领域对象的持久化,可以很方便的使用不同的关系数据库作为后台存储。使用 Play 框架可以很容易的构建使用 REST架构风格的应用。它使用 Groovy 作为视图层模板使用的表达式语言。模板之间的继承机制也可以避免代码的重复。总的来说,Play框架非常适合快速 Web 应用开发。

Play 框架采用经典的 MVC 架构,把 Web 应用分成模型层、控制层和视图层三个层次。每个层次对应的文件被存放在不同的目录下面,方便组织和管理。使用 Play 框架的 Web 应用具有相同的目录结构,如 图 1 所示。


图 1. 使用 Play 框架的 Web 应用的目录结构

如 图 1 所示,应用自身的文件被放在 app 目录下面,三个子目录分别存放的是 MVC 模式的三个层次的内容。其中 modelscontrollers 目录下面是 Java 源文件,而 views 目录下面则是视图层使用的模板文件。conf 目录下面存放的是应用的配置文件、HTTP 路由文件和国际化所需的消息文件。public 目录则是存放 Web 应用的静态文件,包括 JavaScript、CSS 和图像文件等。lib 目录存放所需的额外的 Java 库。test 目录存放的是测试结果。

开发环境

本文中使用的 Play 框架的版本是 1.0.3.1,使用的集成开发环境是 Eclipse 3.6, 使用 Dojo 作为 JavaScript 框架。在 Play 框架官方网站(见 参考资料)下载 Play 框架的压缩包之后,解压到某个目录,并把该目录下面的 bin 目录添加到环境变量中。接着启动一个命令窗口,运行 play new developers_notebook 就可以创建出一个新的名为 developers_notebook 的 Web 项目。在项目目录的父目录下面,运行 play eclipsify developers_notebook 就可以创建出来 Eclipse 工程。通过 Eclipse 导入此工程就可以在 Eclipse 里面进行开发了。Play 框架的 support 目录下的 eclipse 目录下有个名为 org.playframework.playclipse 的 Eclipse 插件,将此插件复制到 Eclipse 的 plugins 目录就可以安装。运行 play run 就可以运行此 Web 应用,访问 http://localhost:9000就可以看到。每次在 Eclipse 里面修改了代码之后,不需要重新启动应用,只需要刷新页面就能看到更新之后的结果。这是 Play 框架的一个非常方便的特性。

本文中的示例应用称为“开发人员记事本”。开发人员可以用它来记录开发过程中的一些注意事项。下面首先介绍 Play 框架中的模型层。


模型层

模型层包含的是 Web 应用中的领域对象。Play 框架推荐的实践是模型层的对象不应该是仅包含 getter/setter方法的简单 Java Beans,而应该有自己的业务逻辑。Play 框架中应用的模型层类可以是任何的 Java 类。与一般的 JavaBeans 不同的是,模型层类使用声明为 public 的域作为对象的属性。Play 框架会自动生成相应的 getter/setter 方法。这样可以使得代码更加简洁。开发人员也可以提供自己的 getter/setter 方法实现。

领域对象持久化

领域对象的实例一般需要持久化下来。最常见的持久化方式就是使用关系数据库。Play 框架使用 JPA规范来进行领域对象的持久化。具体的后台实现使用的是 Hibernate。开发人员只需要使用 JPA规范定义的标注,就可以声明领域的持久化行为。比较好的做法是将领域对象类继承自 Play 框架提供的 play.db.jpa.Model 类。play.db.jpa.Model 类提供了一个域 id作为对象的标识符,也是对应的数据库表中的主键。play.db.jpa.JPASupport 类是 play.db.jpa.Model的父类,提供了一些实用方法用来完成从领域对象到数据库之间的映射。表 1 中列出了一些重要的方法,包括常用的增删改查操作。


表 1. play.db.jpa.JPASupport API 说明
方法说明 create(type, name, params) 用来创建领域对象类的一个实例。参数 type 表示的是领域对象类,类型是 java.lang.Classname 表示的是领域对象类的名称;params表示的是一个包含了实例中属性值的类型为 java.util.Map 的哈希表。 edit(obj, name, params) 用来编辑领域对象类的一个实例。参数 obj 表示的是领域对象实例;参数 nameparams 的含义与 create() 方法的相同。 delete() 用来删除单个领域对象类的实例。 delete(query, params) 用来删除多个领域对象类的实例。参数 query 表示的是检索待删除实例的查询,而 params 表示的是查询所使用的参数。 deleteAll() 用来删除领域对象类的所有实例。 find(query, params) 用来查找领域对象类的实例。参数 query 表示的是查找时所用的查询,而 params 表示的是查询所使用的参数。 findAll() 用来查找领域对象的所有实例。 findById(id) 用来根据标识符查找领域对象的实例。 count(query, params) 用来计算某个查询结果中包含的领域对象的实例数。参数 queryparams 的含义与 find() 方法相同。 save() 用来保存该领域对象实例到数据库中。 all() 用来查找系统中的全部领域对象的实例。

表 1 中列出的方法中,find()all() 方法的返回值是 play.db.jpa.JPASupport.JPAQuery 类的实例,表示一个领域对象实例的查询结果。对于此查询结果,可以进行进一步的操作。具体的操作,如 表 2 所示。


表 2. play.db.jpa.JPASupport.JPAQuery API 说明
方法说明 bind(name, param) 用来绑定一个参数的实际值到查询上。在查询语句中可以使用形式参数作为占位符,参数的实际值可以通过此方法来指定。 fetch() 用来获取此查询的所有记录。 fetch(max) 用来获取此查询的前面 max 条记录。 fetch(page, length) 用来对查询结果进行分页。参数 page 表示当前的页数,从 1 开始;length 表示每页的记录数。 first() 用来返回查询结果中的第一条记录 from(position) 用来设置查询结果中处理的起始位置。参数 position 表示起始位置的序号。该方法的返回结果是一个新的 play.db.jpa.JPASupport.JPAQuery 对象。

使用 表 2 中给出的方法,就可以在领域对象类中添加一些非常实用的方法,而不需要把这些方法添加到额外的服务层中。在示例应用中,Note 这个领域对象类表示的是用户添加的记录。代码清单 1 中给出了 Note 类中的一些实用方法。


清单 1. 领域对象类中的实用方法
            // 创建新的领域对象 Note 的实例,edit() 方法的使用与 create() 类似            Map params = new HashMap();            params.put("note.title", new String[] {"My note"});            params.put("note.content", new String[] {"My note's content"});            Note.create(Note.class, "note", params).save();            // 使用 find() 来进行查找            List notes = Note.find("byTitle", "My note").fetch();            // 使用 findById() 来查找单个实例            Note note1 = Note.findById(1);            // 使用 delete() 来删除对象实例            Note.delete("byTitle", "My note");            // 返回查询结果中的第 2 到第 11 条记录。            Note.find("byTitle", "My note").from(1).fetch(10);            

在介绍完 Play 框架的模型层之后,下面介绍控制层。


控制层

Play 框架中的控制层是模型层和视图层之间的桥梁。控制层负责接收 HTTP请求并返回相应的响应。一般来说,控制层的典型实现是接收到 HTTP请求之后,从请求中获取一些参数,再调用服务层对应的处理方法。服务层的方法会对领域对象进行操作,完成具体的业务逻辑。最后,某种格式的响应被返回给请求者,如 HTML 页面、JSON 数据和 XML 数据等。Play 框架的控制层实现使得完成这样的典型场景变得非常简单。

Play 框架中的每个控制器都是一个普通的 Java 类,继承自 play.mvc.Controller 类,在包 controllers 中。控制器类中的每个公开的静态方法都表示一个动作。每个动作负责完整的请求 / 响应的流程,也就是说,所有前面提到的所有请求/响应的过程都需要在每个动作中来完成。

参数绑定

在控制层实现中很繁琐但是必不可少的操作就是解析 HTTP 请求中的参数。不同的 Web 开发框架会提供自己的参数解析方式。Play框架也提供了相应的支持。Play 框架可以解析 HTTP 请求中查询字符串和 URI路径中包含的以及请求体中以格式编码的参数。所有这些参数都放在 params 对象中,其中包含 get()getAll()put() 等方法用来获取和设置参数的值。除了这种传统的使用方式之外,Play 框架还支持直接把参数的值绑定到动作方法的参数上面。比如一个动作方法的声明是 show(String username),那么请求中的参数 username 的值会在 show() 方法被调用时作为实际参数传递进去。Play 框架会负责完成相应的类型转换。值得一提的是对于日期类型(java.util.Date)的参数,Play 框架支持多种类型的日期格式的转换。比如动作方法的声明是 display(Date postedAt),而请求的格式可能是 /display?postedAt=2010-09-22,Play 框架会自动完成相应的类型转换。

除了常见的基本数据类型之外,Play 框架还支持直接绑定领域对象的实例。比如动作方法的声明是 create(Note note),可以在参数中直接指定对象实例的属性的值。请求的格式可能是 /create?title=Note123&content=Good。Play 框架会负责创建一个 Note 类的实例,并根据参数的值设置该实例的属性 titlecontent 的值。这种绑定方式不仅支持简单对象,还支持嵌套对象和列表。比如 /create?tags[0]=ajax&tags[1]=web 可以设置列表类型属性 tags 的值。

Play 框架的这种绑定方式还支持文件对象,使得上传文件变得非常简单。只需要在表单中添加文件上传的控件()并使用 multipart/form-data编码来提交请求,在动作方法的参数中就可以获取到上传文件对应的 java.io.File 对象。比如动作方法的声明可能是 upload(File picture)。上传的文件被保存在临时目录中,在请求完成之后会被自动删除。可以在动作方法中完成对上传文件的操作。

返回响应结果

在控制层的动作方法完成了与业务逻辑相关的处理之后,需要把响应返回给客户端。响应的结果可能是正确完成,也可能是出现错误。Play 框架提供了方便的实现用来返回不同类型的响应。使用 play.mvc.Controller 类提供的不同方法就可以生成这些响应内容。

  • 请求正确完成,HTTP 状态代码为 200。使用 ok() 方法生成不带内容的响应。使用 render() 方法来生成使用模板的响应。使用 renderText() 方法生成 text/plain 类型的纯文本响应。使用 renderXml() 方法生成 text/xml 类型的 XML 格式的响应。使用 renderJSON() 方法生成 application/json 类型的 JSON 格式的响应。使用 renderBinary() 方法生成二进制内容的响应。
  • 跳转到新的页面,HTTP 状态代码为 3XX。使用 redirect() 方法来跳转到新的 URL。使用 notModified() 方法来返回状态代码 304。
  • HTTP 状态代码 4XX。使用 unauthorized() 方法返回状态代码 401。使用 forbidden() 方法返回状态代码 403。使用 notFound() 方法返回状态代码 404。
  • 服务器内部错误,HTTP 状态代码 5XX。使用 error() 方法返回状态代码 500。

从上面列出的方法可以看出,Play 框架使用一些有意义的方法名称替换掉了难以记忆的 HTTP 状态代码,使用起来更加方便。同时,对于常见的响应格式,包括 HTML、XML、JSON 和二进制内容,都提供了相应的方法,使得开发人员不会遗漏掉响应中 Content-Type 的声明。

方法拦截

控制层的方法通常需要执行一些横切的逻辑,比如用户认证、加载通用信息和记录日志等。在 Spring框架中,这些横切的逻辑是通过面向方面编程(AOP)的支持来实现的。Play框架提供了更加简单易用的方法拦截支持,通过简单的标注就可以定义一些执行拦截操作的方法。这些方法必须非公开的静态方法。Play框架支持的方法拦截标注有 @Before@After@Finally@With 等四种。

@Before 标注的方法在动作方法执行之前被调用。@After 标注的方法在动作方法执行之后被调用。@Finally 标注的方法在动作方法的响应结果已经成功生成之后被调用。这三个标注都支持额外的两个属性:priority 表示标注的方法的优先级,0 为最高;unless 是一个字符串数组,表示不适用此拦截方法的动作方法的名称。如 @Before(unless="index") 表示此拦截方法不会应用在动作方法 index() 上。

如果控制器类中存在继承体系结构的话,父类中声明的拦截方法对于所有子类的动作方法都是适用的。在有些情况下,开发人员可能希望把拦截方法定义在不同的类体系结构中。由于 Java 不支持多继承,无法通过继承的方式来应用来自不同类体系结构上的拦截方法。针对这种情况,Play 框架提供了@With 标注。在控制器类 ControllerA 中定义的拦截方法可以通过 @With 标注来应用到另外一个控制器类 ControllerB 上,而且不通过继承方式来实现。只需要在 ControllerB 中声明 @With(ControllerA.class) 即可。

在介绍完 Play 框架的控制层之后,下面介绍视图层。


视图层

Web 开发框架的使用者都习惯于使用某种模板技术来生成 HTML 页面,这些技术包括常见的 JSP、ASP 和 PHP 等。Play框架也提供了自己的模板技术,可以用来动态的创建 HTML、XML、JSON 以及其它文本类型的内容。Play 框架的模板技术使用的是Groovy 语言。Groovy 语言的灵活性和简洁性使得 Play框架的模板简单而且易用。在模板中可以混用静态内容和生成动态内容的各种元素。在模板中可以使用的动态元素如 表 3 所示。


表 3. 模板中可用的动态元素
动态元素说明 ${...} 用来对一个表达式进行求值。如 ${note.title} 的值是领域对象 note 的属性 title 的值。 @{...}@@{...} 用来生成调用控制器中动作方法的 URL,可以用在页面的链接中。@{...}@@{...} 生成的分别是相对 URL 和绝对 URL。如 首页 生成一个指向首页的链接。 &{...} 用来显示经过国际化之后的消息内容。 *{...}* 用来添加注释。如 *{ 这是注释 }*%{...}% 用来添加复杂的 Groovy 脚本,可以声明变量和添加语句。 #{...} 用来调用 Play 框架的或是开发人员自定义的标签。

Play 框架中的标签的作用相当于 JSP 中的标签。Play 框架本身提供一些常用的标签,开发人员也可以根据需要开发自己的标签。Play 框架内置提供的标签说明如 表 4 所示。


表 4. Play 框架提供的标签
标签说明 a 用来生成指向控制器中动作方法的 HTML 链接元素。如 #{a @Application.index()} 首页 #{/a}ififnotelseifelse 用来进行条件判断。 setget 用来设置和获取可以在模板中使用的变量。如 #{set email:'alex@example.org'} 设置了变量 email 的值,可以通过 #{get 'email'} 来获取。 script 用来生成一个