玩转play framework ( by quqi99 )

                                         玩转play framework ( by quqi99 )

 

作者:张华  发表于:2011-04-06
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明

( http://blog.csdn.net/quqi99 )

p { margin-bottom: 0.21cm; }h1 { margin-bottom: 0.21cm; }h1.western { font-family: "Arial",sans-serif; font-size: 16pt; }h1.cjk { font-family: "宋体"; font-size: 16pt; font-style: normal; font-weight: bold; }h1.ctl { font-family: "Tahoma"; font-size: 16pt; font-weight: bold; }h2 { margin-bottom: 0.21cm; }h2.western { font-family: "Arial",sans-serif; font-size: 14pt; font-style: italic; }h2.cjk { font-family: "宋体"; font-size: 14pt; font-style: italic; }h2.ctl { font-family: "Tahoma"; font-size: 14pt; font-style: italic; }pre { font-family: "Courier New",monospace; }h3 { margin-bottom: 0.21cm; }h3.western { font-family: "Arial",sans-serif; }h3.cjk { font-family: "宋体"; font-style: normal; }h3.ctl { font-family: "Tahoma"; }a:link { color: rgb(0, 0, 255); }

 



内容目录

1 开发环境构建 1

1.1 下载二进制发布包 1

1.2 创建应用 1

1.3 集成eclipse 2

2 Hello World 2

3 Play 程序开发 3

3.1 视图层 3

3.1.1 模板元素 3

3.1.2 模板继承 3

3.2 控制层 3

3.3 模型层 4

3.4 无状态的体系结构 4

3.5 任务调度 5

3.6 支持non-web 应用 5

3.7 支持CRUD 应用 8

3.8 支持安全 9


 

1 开发环境构建

1.1 下载二进制发布包

http://download.playframework.org/releases/play-1.2.1.zip

下载完后解压,例如我解压的目录是: /home/workspace/play-1.2.1

1.2 创建应用

zhanghua:play-1.2.1 root# ./play new QATool

~ _ _

~ _ __ | | __ _ _ _| |

~ | '_ /| |/ _' | || |_|

~ | __/|_|/____|/__ (_)

~ |_| |__/

~

~ play! 1.2.1, http://www.playframework.org

~

~ The new application will be created in /home/workspace/play-1.2.1/QATool

~ What is the application name? [QATool]

~

~ OK, the application is created.

~ Start it with : play run QATool

~ Have fun!

~

zhanghua:play-1.2.1 root# ./play run QATool

~ _ _

~ _ __ | | __ _ _ _| |

~ | '_ /| |/ _' | || |_|

~ | __/|_|/____|/__ (_)

~ |_| |__/

~

~ play! 1.2.1, http://www.playframework.org

~

~ Ctrl+C to stop

~

Listening for transport dt_socket at address: 8000

10:44:48,951 INFO ~ Starting /home/workspace/play-1.2.1/QATool

10:44:49,774 WARN ~ You're running Play! in DEV mode

10:44:49,876 INFO ~ Listening for HTTP on port 9000 (Waiting a first request to start) ...

 

这时,你能在浏览器上访问 http://localhost:9000 验证

1.3 集成 eclipse

zhanghua:play-1.2.1 root# ./play eclipsify QATool

 

然后,能过 File/Import/General/Existing project… QATool 应用导入到 eclipse 中。

其次,要安装 Eclipse 插件,在 support/eclipse/ 目录下,要安装它,只需将你在该目录下找到的 JAR 文件,简单地复制到 Eclipse 安装目录的 dropins 文件夹下。

说明一下, eclipsify 命令生成若干个应用程序的启动器( launcher ),主启动器( main launcher )只能通过 Eclipse Run As 命令使用。可以随时通过 Debug As 启动一个调试会话,然后使用 Connect JPDA launcher ,停止调试会话并不会导致服务器终止。如果你对应用程序做了任何重要的修改,如改变 classpath ,则需要使用 eclipsify 重新生成 Eclipse 配置文件。

在调试时,首先用 ./play run QATool 命令启动应用 ; 然后连接 JPDA 调试器即可。

在启动应用时,也可以先将所有 conf 目录、应用本身的 JAR 包、及 play 本身的 JAR 包加到环境变量后再执行下列命令即可 ( 通过 run as 启动,而不是 debug as)

java -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

-Dapplication.path="${project_loc:QATool}"

-javaagent:"/home/workspace/play-1.2.1/framework/play-1.2.1.jar"

play.server.Server

2 Hello World

  1. vim app/views/Application/index.html

#{extends 'main.html' /}

#{set title:'Home' /}

 

#{if flash.error}

< p style =" color : #c00 " >

${flash.error}

</ p >

#{/if}

< form action = "@{Application.sayHello()}" method = "GET" >

< input type = "text" name = "myName" />

< input type = "submit" value = "Say hello!" />

</ form >

 

2 vim app/controllers/Application.java 添加一方法:

 

public static void sayHello( @Required String myName) {

if ( validation . hasErrors ()) {

flash .error( "Oops, please enter your name!" );

index ();

}

render (myName);

}

 

3 vim app/views/Application/sayHello.html

#{extends 'main.html' /}

       #{set title:'Home' /}


 

       <h1>Hello ${myName ?: 'guest'}!</h1>


 

       <a href="@{Application.index()}">Back to form</a>

3 Play 程序开发

3.1 视图层

3.1.1 模板元素

Play 框架的模板技术使用的是 Groovy 语言。在模板中可以使用的动态元素如下说明:

${...} 用来对一个表达式求值,如 ${note.title} 是指取 note 对象的 title 属性。

@{...} @@{...} 是指 JAVA 端控制器动作方法的相对 URL 及绝对 URL

&{...} 用来显示经国际化之后的消息内容。

*{...}* 注释

%{...}% 用来添加复杂的 Groovy 脚本,可以声明变量和添加语句。

#{...} 用来调用 Play 框架或自定义的标签。


举个例子,如对 topics 变量进行循环迭代,则用 Groovy 模板,如下:

%{ for(topic in topics){ }%

<td> ${topic.id} </td>

%{ } }%

3.1.2 模板继承

Play 框架中可以使用 #{extends} #{doLayout} 来实现模板之间的继承。

在父模板中可以包含任意的内容。在需要由子模板填充的位置,使用 #{doLayout /} 进行声明即可。在子模板中通过 #{extends} 来声明所继承的模板。如 #{extends 'main.html'} 就声明继承自模板 main.html 。当子模板被生成之后,将包含父模板中的内容。而子模板中只需要定义扩展的内容即可。

3.2 控制层

在控制层的动作方法完成了与业务逻辑相关的处理之后,需要把响应返回给客户端。响应的结果可能是正确完成,也可能是出现错误。 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


3.3 模型层

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

举些例子:

1 )分页查询:

List<Content> list = Content. find ( " status=? order by id asc" , status).from(startPos).fetch(pageSize);

2 )查询数目

Content. count ( "status = ?" , status);

3 )保存 user.save();

4 )更新

User user = Dao. findByUsername ( "quqi99" );

user.setPassword( "111111" );

user.save();

 

可参考:
Play! 连接 MySQL 配置 

 
http://c4fun.iteye.com/blog/506959 

 

http://www.playframework.org/documentation/1.1.1/jpa 

 

3.4 无状态的体系结构

HTTP 协议本身就被设计成无状态的,采用请求 / 响应的模式。不同的请求之间并不存在相互关系。但是这种架构模式在开发某些 Web 应用的时候不是很方便。有些应用要求用户进行认证登录之后才能进行某些操作。同样的 URL ,认证和未认证用户看到的内容是不同的。而且用户认证成功之后,他应该在一段时间内保持这种认证状态。否则的话,用户每次都需要输入用户名和密码才 能访问受限的内容。对于这种情况,很多 Web 开发框架提供了会话的支持,允许应用保存一些与会话相关的数据。 Java Servlet 规范中的 javax.servlet.http. HttpSession 就是一种会话的接口。应用的服务器会负责维护每个会话相关的数据。这些数据可以通过一个会话 ID 来进行标识。这个标识会利用浏览器的 cookie 机制保存在浏览器端,也可以作为请求 URL 的参数来传递。服务器端通过此标识来识别每个会话。在处理相应的请求的时候,就可以根据会话 ID 来获取保存在服务器端上的会话数据。会话机制的问题是会影响应用的可伸缩性。如果一个应用使用多台服务器的话,就需要额外的机制来保证同一用户在不同机器 上面的会话是同步的。而无状态的实现 则不存在这个问题,对于某一个请求,由不同机器来处理的结果都是相同的。

Play 框架的设计架构就是无状态的。它没有提供服务器端的机制用来维护跨多个请求的数据。如果确实需要保存这样的数据的话,可以考虑下面几种方案:

  • 保存在 Session Flash 作用域中。 Play 框架中仍然有会话的机制,但是并没有提供在服务器端保存会话数据的能力。会话数据是保存在浏览器的 cookie 中的,由浏览器在每次请求的时候自动发送。通过这种方式来达到维护会话数据的目的。 由于会话数据是保存在 cookie 中,其大小是有限制的,一般不能超过 4K 字节,而且只能保存字符串类型的数据。 Flash 作用域和会话一样,也是通过 cookie 来保存的。所不同的是, Flash 作用域中的数据只在下次请求中是有效的。

  • 保存在持久化的数据存储中,如数据库中。如果需要在多个请求中使用同一个领域对象的话,可以把这个对象的 ID 保存在 Session Flash 作用域中,而在控制器动作方法中使用此 ID 来从数据库中查询相应的对象。

  • 保存在暂时性数据存储中,如缓存中。 Play 框架内置了缓存的支持,通过调用类 play.cache.Cache 就可以对缓存进行操作。与使用持久化存储类似,缓存中的键的值可以保存在 Session Flash 作用域中。

对于熟悉了 Java Servlet 规范的开发人员来说,需要一些时间来适应 Play 框架的这种无状态的体系结构。不过这种结构对于应用的可伸缩性来说,确实是非常有好处的。

3.5 任务调度

Web 应用开发中,有时候会需要定期执行一些调度任务,比如数据库备份和数据同步等。这些任务不是通过 HTTP 请求来触发的,而是定时执行的。 Play 框架提供了内置的任务调度支持的能力。创建新任务的时候,只需要继承自 play.jobs.Job 类,并覆写 doJob() 方法即可。如果要创建的任务有返回结果的话,覆写 doJobWithResult() 方法即可。任务创建完成之后,可以选择不同的调度方式。一种方式是在应用启动的时候执行一次。只需要在任务的 Java 类上添加标注 @OnApplicationStart 即可。对于定期执行的任务, Play 框架提供了两个标注:一个是 @Every ,用来按照固定的时间间隔调度任务,如 @Every("1h") 声明任务每个小时执行一次;另外一个是 @On ,用来声明描述调度策略的 CRON 表达式。

3.6 支持 non-web 应用

对于 non-web 应用,如果想要重要模型层的话,可用如下代码:

package nonwebapp;

 

import java.io.File;

import java.lang.reflect.Method;

import java.util.List;

 

import models.Content;

import play.Play;

import play.db.DBPlugin;

import play.db.jpa.JPAPlugin;

import play.test.Fixtures;

 

/**

*

* author: huazhang since : 2011 - 6 - 21

*/

public abstract class PlayLoaderMain

{

 

public static void main(String[] args) throws Exception

{

try

{

String appPath = "/home/workspace/play-1.2.1/QATool" ;

File root = new File(appPath);

Play. init (root, System. getProperty ( "play.id" , "" ));

 

Thread. currentThread ().setContextClassLoader(Play. classloader );

Class<?> c = Play. classloader .loadClass( "nonwebapp.BuildAccounts" );

Method m = c.getMethod( "run" );

m.invoke(c.newInstance());

} catch (Exception e)

{

e.printStackTrace();

}

}

 

public void run() throws Exception

{

new DBPlugin().onApplicationStart();

new JPAPlugin().onApplicationStart();

JPAPlugin. startTx ( true );

 

exec();

 

JPAPlugin. closeTx ( false );

}

 

public abstract void exec() throws Exception;

}



package nonwebapp;

 

import java.util.List;

 

import models.Content;

 

/**

*

* author: huazhang since : 2011 - 6 - 22

*/

public class BuildAccounts extends PlayLoaderMain

{

@Override

public void exec() throws Exception

{

List<Content> list = Content. findAll ();

System. out .println( "" );

}

}

 

再写个脚本调用它,脚本中加环境变量

 

#!/bin/bash

# huazhang

# 2011-06-28

#

 

# resolve links - $0 may be a softlink

THIS="$0"

while [ -h "$THIS" ]; do

ls =` ls - ld "$THIS"`

link=` expr "$ ls " : '.*-> /(.*/)$'`

if expr "$link" : '.*/.*' > / dev /null; then

THIS="$link"

else

THIS=` dirname "$THIS"`/"$link"

fi

done

 

# some directories

THIS_DIR=` dirname "$THIS"`

QATOOL_HOME=` cd "$THIS_DIR" ; pwd `

PLAY_HOME=` cd "$QATOOL_HOME/.." ; pwd `

 

if [ $# - lt 1 ] ; then

echo "USAGE: $0 class"

echo " e.g.: $0 nonwebapp.ImportData"

exit 1;

fi

 

if [ "$JAVA_HOME" = "" ]; then

echo "Error: JAVA_HOME is not set."

exit 1

fi

 

JAVA=$JAVA_HOME/bin/java

JAVA_HEAP_MAX=-Xmx1000m

 

for f in ${PLAY_HOME}/framework/*.jar; do

CLASSPATH=${CLASSPATH}:$f;

done

for f in ${PLAY_HOME}/framework/ lib /*.jar; do

CLASSPATH=${CLASSPATH}:$f;

done

 

if [ -d "${QATOOL_HOME}/ conf " ]; then

CLASSPATH=${CLASSPATH}:${QATOOL_HOME}/ conf

fi

for f in $QATOOL_HOME/ lib /*.jar; do

CLASSPATH=${CLASSPATH}:$f;

done

if [ -d "${QATOOL_HOME}/ tmp /classes" ]; then

CLASSPATH=${CLASSPATH}:${QATOOL_HOME}/ tmp /classes

fi

 

# default log directory & file

if [ "$QATOOL_LOGFILE" = "" ]; then

QATOOL_LOGFILE="$QATOOL_HOME/logs/QATool.log"

fi

 

if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then

QATOOL_OPTS="$QATOOL_OPTS -Djava.library.path=$JAVA_LIBRARY_PATH"

fi

 

# run it

JAVA_OPTS="-server -Xms1600m -Xmx1600m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true"

exec "$JAVA" $QATOOL_OPTS - classpath "$CLASSPATH" "$@"

3.7 支持 CRUD 应用

1) vim application.conf

# Import the crud module

module.crud =${play.path}/modules/crud

2) vim routes

# Import CRUD routes

* /admin module:crud

3) 改动了配置,要重新运行下列命令:

zhanghua:play-1.2.1 root# ./play eclipsify QATool

4) 新建 Control

package controllers;

/**

*

* author: huazhang

* since : 2011 - 6 - 27

*/

public class Users extends CRUD

{

}

5) 新建 Model

/**

*

* author: huazhang

* since : 2011 - 6 - 27

*/

@Entity

public class User extends Model

{

@Email

@Required

@MaxSize (256)

public String username ;

@Required

public String password ;

@Required

public String role ;

  1. 启动后访问 http://localhost:9000/admin 即可

7) 客户化字符串 , vim conf/messages, 添加:

username= 用户名

password= 密码

role= 角色

8) 客户化表现层,执行下列命令创建 /app/views/Users/list.html

zhanghua:play-1.2.1 root# ./play crud:ov --template Users/list

3.8 支持安全

http://www.playframework.org/documentation/1.0/secure

1) vim application.conf

# Import the secure module

module.secure=${play.path}/modules/secure

2) vim routes

# Import Secure routes

     * / module:secure

3) 改动了配置,要重新运行下列命令:

zhanghua:play-1.2.1 root# ./play eclipsify QATool

4) 修改 Controller, 添加 With Check 两个标签:

@With (Secure. class )

public class Application extends Controller {

 

@Check (Constant. ROLE_ADMIN )

public static void index() {

String user = Security. connected ();

System. out .println( "" );

render ();

}

5) 新建一个自定义 Security 类,如下:

/**

*

* author: huazhang

* since : 2011 - 6 - 28

*/

public class Security extends Secure.Security

{

static boolean authentify(String username, String password)

{

User user = Dao. findByUsername (username);

return user != null && user. password .equals(password);

}

static boolean check(String role) {

String loginUser = connected ();

User user = Dao. findByUsername (loginUser);

return user== null ? false : role.equalsIgnoreCase(user.getRole());

}

}

你可能感兴趣的:(eclipse,框架,服务器,任务调度,groovy,web应用开发)