作者:张华 发表于:2011-04-06
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明
( http://blog.youkuaiyun.com/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
2Hello World 2
3Play程序开发 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.1root# ./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.1root# ./play run QATool
~ _ _
~ _ __ | | __ _ _ _| |
~| '_ /| |/ _' | || |_|
~| __/|_|/____|/__ (_)
~|_| |__/
~
~play! 1.2.1, http://www.playframework.org
~
~Ctrl+C to stop
~
Listeningfor transport dt_socket at address: 8000
10:44:48,951INFO ~ Starting /home/workspace/play-1.2.1/QATool
10:44:49,774WARN ~ You're running Play! in DEV mode
10:44:49,876INFO ~ Listening for HTTP on port 9000 (Waiting a first request tostart) ...
这时,你能在浏览器上访问http://localhost:9000验证
1.3集成eclipse
zhanghua:play-1.2.1root# ./play eclipsify QATool
然后,能过File/Import/General/Existingproject… 将QATool应用导入到eclipse中。
其次,要安装Eclipse插件,在support/eclipse/目录下,要安装它,只需将你在该目录下找到的JAR文件,简单地复制到Eclipse安装目录的dropins文件夹下。
说明一下,eclipsify命令生成若干个应用程序的启动器(launcher),主启动器(mainlauncher)只能通过Eclipse的 RunAs 命令使用。可以随时通过DebugAs 启动一个调试会话,然后使用ConnectJPDA launcher ,停止调试会话并不会导致服务器终止。如果你对应用程序做了任何重要的修改,如改变classpath,则需要使用eclipsify重新生成Eclipse配置文件。
在调试时,首先用./playrun QATool 命令启动应用;然后连接JPDA调试器即可。
在启动应用时,也可以先将所有conf目录、应用本身的JAR包、及play本身的JAR包加到环境变量后再执行下列命令即可(通过runas启动,而不是debugas):
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
2Hello World
-
vimapp/views/Application/index.html
#{extends'main.html' /}
#{settitle:'Home' /}
#{ifflash.error}
<pstyle="color:#c00">
${flash.error}
</p>
#{/if}
<formaction="@{Application.sayHello()}"method="GET">
<inputtype="text"name="myName"/>
<inputtype="submit"value="Sayhello!" />
</form>
2)vim app/controllers/Application.java 添加一方法:
publicstaticvoidsayHello(@RequiredStringmyName) {
if(validation.hasErrors()){
flash.error("Oops,please enter your name!");
index();
}
render(myName);
}
3)vimapp/views/Application/sayHello.html
#{extends'main.html' /}
#{set title:'Home' /}
<h1>Hello ${myName ?: 'guest'}!</h1>
<a href="@{Application.index()}">Back to form</a>
3Play
程序开发
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
)更新
Useruser = 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开发框架提供了会话的支持,允许应用保存一些与会话相关的数据。JavaServlet 规范中的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作用域中。
对于熟悉了 JavaServlet 规范的开发人员来说,需要一些时间来适应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;
importjava.io.File;
importjava.lang.reflect.Method;
importjava.util.List;
importmodels.Content;
importplay.Play;
importplay.db.DBPlugin;
importplay.db.jpa.JPAPlugin;
importplay.test.Fixtures;
/**
*
*author: huazhangsince : 2011-6-21
*/
publicabstractclassPlayLoaderMain
{
publicstaticvoidmain(String[] args) throwsException
{
try
{
StringappPath = "/home/workspace/play-1.2.1/QATool";
Fileroot = newFile(appPath);
Play.init(root,System.getProperty("play.id",""));
Thread.currentThread().setContextClassLoader(Play.classloader);
Class<?>c= Play.classloader.loadClass("nonwebapp.BuildAccounts");
Methodm = c.getMethod("run");
m.invoke(c.newInstance());
}catch(Exception e)
{
e.printStackTrace();
}
}
publicvoidrun() throwsException
{
newDBPlugin().onApplicationStart();
newJPAPlugin().onApplicationStart();
JPAPlugin.startTx(true);
exec();
JPAPlugin.closeTx(false);
}
publicabstractvoidexec() throwsException;
}
package
nonwebapp;
importjava.util.List;
importmodels.Content;
/**
*
*author: huazhangsince : 2011-6-22
*/
publicclassBuildAccounts extendsPlayLoaderMain
{
@Override
publicvoidexec() throwsException
{
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": '.*-> /(.*/)$'`
ifexpr"$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[ $# -lt1 ] ; then
echo"USAGE: $0 class"
echo" e.g.: $0 nonwebapp.ImportData"
exit1;
fi
if[ "$JAVA_HOME" = "" ]; then
echo"Error: JAVA_HOME is not set."
exit1
fi
JAVA=$JAVA_HOME/bin/java
JAVA_HEAP_MAX=-Xmx1000m
forf in ${PLAY_HOME}/framework/*.jar; do
CLASSPATH=${CLASSPATH}:$f;
done
forf in ${PLAY_HOME}/framework/lib/*.jar;do
CLASSPATH=${CLASSPATH}:$f;
done
if[ -d "${QATOOL_HOME}/conf"]; then
CLASSPATH=${CLASSPATH}:${QATOOL_HOME}/conf
fi
forf 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.1root# ./play eclipsify QATool
4)
新建Control
package
controllers;
/**
*
*author: huazhang
*since : 2011-6-27
*/
publicclassUsers extendsCRUD
{
}
5)新建Model
/**
*
*author: huazhang
*since : 2011-6-27
*/
@Entity
publicclassUser extendsModel
{
@Required
@MaxSize(256)
publicString username;
@Required
publicString password;
@Required
publicString role;
-
启动后访问http://localhost:9000/admin即可
7)客户化字符串,vim conf/messages, 添加:
username=用户名
password=密码
role=角色
8)客户化表现层,执行下列命令创建/app/views/Users/list.html
zhanghua:play-1.2.1root# ./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.1root# ./play eclipsify QATool
4)
修改Controller,
添加With
及Check
两个标签:
@With
(Secure.
class
)
publicclassApplication extendsController {
@Check(Constant.ROLE_ADMIN)
publicstaticvoidindex() {
Stringuser= Security.connected();
System.out.println("");
render();
}
5)新建一个自定义Security类,如下:
/**
*
*author: huazhang
*since : 2011-6-28
*/
publicclassSecurity extendsSecure.Security
{
staticbooleanauthentify(String username, String password)
{
Useruser = Dao.findByUsername(username);
returnuser != null&& user.password.equals(password);
}
staticbooleancheck(String role) {
StringloginUser = connected();
Useruser = Dao.findByUsername(loginUser);
returnuser==null? false: role.equalsIgnoreCase(user.getRole());
}
}