中英文对照 介绍Play Framework 框架的控制器(Controller)

红薯 发布于 2010/07/07 17:41
阅读 6K+
收藏 14


下面内容来自 Play 的官方文档中关于控制器一节的内容。

翻译:红薯 (http://my.oschina.net/javayou)

Play Framework 框架的控制器(Controller)

Business logic is managed in the domain model layer. As a client (typically a web browser) cannot directly invoke this code, the functionality of a domain object is exposed as resources represented by URIs.


A client uses the uniform API provided by the HTTP protocol to manipulate these resources, and by implication the underlying business logic. However, this mapping of resources to domain objects is not a bijection: the granularity can be expressed at different levels, some resources may be virtuals, for some resources aliases may be defined…

客户端使用 HTTP 协议中提供的统一方法来访问这些特定资源,并隐式调用底层的业务逻辑。但是这种URI资源到域对象之间的映射关系并不是双向的,其粒度可以使用不同的层次来表示。某些资源可以是虚拟的,也可以给资源定义一个别名…

This is precisely the role played by the Controller layer: providing a glue between the domain model objects and transport layer events. As the Model layer, controllers are written in pure Java, making it easy to access or modify Model objects. Like the HTTP interface, Controllers are procedural and Request/Response oriented.


The Controller layer reduces the impedance mismatch between HTTP and the Domain Model.

控制器层可减少由于 HTTP 协议和域模型之间不匹配的障碍。



There are different architectural models with different strategies. Some protocols give you direct access to the domain model objects. This is typically what EJB or Corba protocols do. In these cases, the architectural style used is RPC (Remote Procedure Call). These communication styles are hardly compatible with web architecture.

不同的体系架构有着不同的设计策略,某些协议可以让你直接访问域模型对象,例如 EJB 和 CORBA 协议就是这么做的。在这种情况下,使用的 RPC 远程过程调用的设计风格,这种设计风格很难跟 Web 应用兼容。

Some technologies like SOAP try to give access to the model object domain through the Web. However, SOAP is just another RPC-style protocol, in this case using HTTP as a transport protocol. It is not an application protocol.

而另外一些技术例如 SOAP 试图通过 Web 来访问域对象模型,但不管怎样,SOAP 还是 RPC 风格的设计,尽管使用的是 HTTP 传输协议,这并不是应用的协议。

The web’s principles are not fundamentally object-oriented. So a layer is needed to adapt HTTP to your favorite language.

从根本上来说,Web 的原则并不是面向对象的,因此需要引入一个用来将 HTTP 协议转化为你喜好的编程语言的层,这就是控制层。

A controller overview


A Controller is a Java class, hosted by the controllers package, and subclassing play.mvc.Controller.

在 Play 框架中,控制器其实就是一个 Java 类,位于 controllers 包中,继承了父类 play.mvc.Controller。

This is a Controller:


package controllers;
import models.Client;
import play.mvc.Controller;
public class Clients extends Controller {
    public static void show(Long id) {
        Client client = Client.findById(id);
    public static void delete(Long id) {
        Client client = Client.findById(id);

Each public, static method in a Controller is called an action. The signature for an action method is always:

在这个示例控制器中,每一个被声明为 public static 的方法被称为 Action,每个 Action 的形式如下所示:

public static void action_name(params...);

You can define parameters in the action method signature. These parameters will be automatically resolved by the framework from the corresponding HTTP parameters.

你可以为 Action 定义各种参数,这些参数会自动与 HTTP 的参数进行绑定和赋值。

Usually, an action method doesn’t include a return statement. The method exit is done by the invocation of a result method. In this example, render(…) is a result method that executes and displays a template.

一般情况下,Action 方法无需返回任何值,该方法通过调用一个结果方法来结束执行,例如 render 就是用来执行并显示一个页面模板。

Retrieving HTTP parameters

获取 HTTP 参数

An HTTP request contains data. This data can be extracted from:

    * The URI path: in /clients/1541, 1541 is dynamic part of the URI Pattern.
    * The Query String: /clients?id=1541.
    * The request body: if the request was sent from an HTML form, the request body contains the form data encoded as x-www-urlform-encoded.

在 HTTP 的请求中包含各种参数数据,例如:

    * URI路径: /clients/1541, 1541 是一种动态的URI路径模式
    * Query String: /clients?id=1541.
    * POST方式的请求,该请求来自某个表单的提交,数据编码方式x-www-urlform-encoded.

In all cases, Play extracts this data and builds a Map<String, String[]> which contains all the HTTP parameters. The key is the parameter name. The parameter name is derived from:

    * The name of the dynamic part of the URI (as specified in the route)
    * The name portion of a name-value pair taken from the Query String
    * The contents of a x-www-urlform-encoded body.

在所有的这些情况中,Play 框架解析出这些数据,并将数据保存在一个 Map<String,String[]> 类型的变量中,Map 的 key 就是参数名,而参数名来自于:

    * routes 中定义的名称,针对于例如 /clients/1541 这种URI请求
    * Query String 中的参数名
    * 来自x-www-urlform-encoded 内容的参数名

Using the params map


The params object is available to any Controller class (it is defined in the play.mvc.Controller super class). This object contains all the HTTP parameters found for the current request.

For example:

参数对象对所有控制器类都是可访问的,在 play.mvc.Controller 类中定义,该对象包含了当前请求的所有参数。


public static void show() {
    String id = params.get("id");
    String[] names = params.getAll("names");

You can also ask Play to do the type conversion for you:

你也可以让 Play 框架帮你做类型转换:

public static void show() {
    Long id = params.get("id", Long.class);

But wait, there are better ways to do this :)

但是等等,别急,还有更好的方法来获取参数 

From the action method signature

You can retrieve HTTP parameters directly from the action method signature. The Java parameter’s name must be the same as the HTTP parameter’s.

For example, in this request:

通过 action 方法

你可以通过 action 方法来直接获取 HTTP 的请求参数,只需要保证 action 方法的参数名和 HTTP 参数名一致即可。



An action method can retrieve the id parameter value by declaring an id parameter in its signature:

要获取这个 id 参数,我们只需要编写如下的 action 方法即可:

public static void show(String id) {

You can use other Java types than String. In this case the framework will try to cast the parameter value to the correct Java type:

我们也可以指定不同的参数类型,而不仅仅是字符串类型,例如下面的代码将参数转成一个 Long 对象,而 Play 框架会自动帮我们实现数据的类型转换:

public static void show(Long id) {

If the parameter is multivalued, you can declare an Array argument:


public static void show(Long[] id) {
    for(String anId : id) {

or even a collection type:

也可以是集合类型,例如 List:

public static void show(List<Long> id) {
    for(String anId : id) {



If the HTTP parameter corresponding to the action method argument is not found, the corresponding method argument is set to its default value (typically null for objects and 0 for primitive numeric types). If a value is found but can’t be properly cast to the required Java type, an error is added to the validation error collection and the default value is used.

如果与 action 方法中的参数没有找到对应的 HTTP 参数,那么 Play 框架会给这个参数设置一个默认值,例如 null 或者数值的 0;但如果action的参数定义为数值类型,而 HTTP 参数确实一个字符串无法转成数值时,那么将会生成一个错误的验证信息,并给这个数值参数设置为默认值

Advanced HTTP to Java binding
Simple types

高级HTTP 到 Java 的绑定

All the native and common Java type are automatically bound:

所有 Java 自带的一些简单类型(如下所示),Play 框架都可以实现自动转换:

int, long, boolean, char, byte, float, double, Integer, Long, Boolean, Char, String, Float, Double.

Note that if the parameter is missing in the HTTP Request, or if automatic conversion fails, Object type will be set to null and native types will be set to their default value.



A date object can be automatically bound if the date’s string representation matches one of the following patterns:



    * yyyy-MM-dd’T’hh🇲🇲ss’Z' // ISO8601 + timezone
    * yyyy-MM-dd’T’hh🇲🇲ss" // ISO8601
    * yyyy-MM-dd
    * yyyyMMdd’T’hhmmss
    * yyyyMMddhhmmss
    * dd'/‘MM’/'yyyy
    * dd-MM-yyyy
    * ddMMyyyy
    * MMddyy
    * MM-dd-yy
    * MM'/‘dd’/'yy

For example:



public static void articlesSince(Date from) {
    List<Article> articles = Article.findBy("date >= ?", from);



File upload is easy with Play. Use a multipart/form-data encoded request to post files to the server, and then use the java.io.File type to retrieve the file object:

在 Play 框架中处理文件上传是非常简单的事,一个 multipart/form-data 编码的请求发送到 Play 服务器,Play 框架会自动为该上传文件生成一个 File 对象:

public static void create(String comment, File attachment) {
    String s3Key = S3.post(attachment);
    Document doc = new Document(comment, s3Key);

The created file has the same name as the original file. It’s stored in a temporary directory and deleted at the end of the request. So you have to copy it in a safe directory or it will be lost.

而且File 对象的文件名跟上传文件原来的名称一致,它被存放在临时目录中,一旦请求结束会自动删除,因此如果要长期使用这个文件,请保存到安全的目录。

Arrays or collections of supported types


All supported types can be retrieved as an Array or a collection of objects:


public static void show(Long[] id) {


public static void show(List<Long> id) {


public static void show(Set<Long> id) {

POJO object binding

POJO 对象绑定

Play also automatically binds any of your model classes using the same simple naming convention rules.

Play 可自动将模型类与请求参数绑定,绑定的规则是类属性名与参数名一致:

public static void create(Client client ) {

A query string to create a client using this action would look like:



Play creates a Client instance and resolves HTTP parameters name to properties on the Client object. Unresolved parameters are safely ignored. Types mismatches are also safely ignored.

Parameter binding is done recursively, which means you can address complete object graphs:

Play 会自动创建一个 Client 的实例,并将对应的参数赋值给该实例的属性,一些无法解析的参数会被忽略,类型不匹配也会被忽略。



In order to update a list of model objects, use array notation and reference the object’s ID. For example imagine the Client model has a list of Customer models declared as List customers. To update the list of Customers you would provide a query string like the following:



JPA object binding

JPA 对象绑定

You can automatically bind a JPA object using the HTTP to Java binding.

你可以使用 HTTP 到 Java 的绑定来自动绑定一个 JPA 对象。

You can provide the user.id field yourself in the HTTP parameters. When Play find the id field, it loads the matching instance from the database before editing it. The other parameters provided by the HTTP request are then applied. So you can save it directly.

通过在 HTTP 参数中提供 user.id 属性,Play 可以在编辑前,从数据库中加载匹配的实例,然后除 id 外的其他参数将会被更新到数据库中,如下面代码:

public static void save(User user) {
    user.save(); // ok with 1.0.1

Result types


An action method has to generate an HTTP response. The easiest way to do this is to emit a Result object. When a Result object is emitted, the normal execution flow is interrupted and the method returns.

For example:

Action 方法是需要生成 HTTP 回应内容的,最简单的方法就是直接通过 render 封装一个结果对象。


public static void show(Long id) {
    Client client = Client.findById(id);
    System.out.println("This message will never be displayed !");

The render(…) method emits a Result object and stops further method execution.

Render 方法输送一个结果对象,并停止 action 方法的执行。

Return some textual content


The renderText(…) method emits a simple Result event which writes some text directly to the underlying HTTP Response.


可使用 renderText 方法来向浏览器返回一些文本内容。


public static void countUnreadMessages() {
    Integer unreadMessages = MessagesBox.countUnreadMessages();

You can format the text message using the Java standard formatting syntax:

也可以通过 Java 标准的格式化语法来对输出的文本进行格式处理:

public static void countUnreadMessages() {
    Integer unreadMessages = MessagesBox.countUnreadMessages();
    renderText("There are %s unread messages", unreadMessages);

Execute a template


If the generated content is complex, you should use a template to generate the response content.


public class Clients extends Controller {
    public static void index() {

A template name is automatically deduced from the Play conventions. The default template path is resolved using the Controller and action names.

In this example the invoked template is:

Play 框架的惯例,模板文件名是跟 action 相对应的,默认的对应方式是使用控制器名和 action 名。



Add data to the template scope

Often the template needs data. You can add these data to the template scope using the renderArgs object:


模板文件是需要数据来执行的,我们可以通过 renderArgs 方法来向模板输送对象:

public class Clients extends Controller {
    public static void show(Long id) {
        Client client = Client.findById(id);
        renderArgs.put("client", client);

During template execution, the client variable will be defined.

For example:



<h1>Client ${client.name}</h1>

A simpler way to add data to the template scope

You can pass data directly to the template using render(…) method arguments:

另外一种更简单的向模板传递数据的方式是直接通过 render 方法:

public static void show(Long id) {
    Client client = Client.findById(id);

In this case, the variables accessible by the template have the same name as the local Java variables.


You can pass more than one variable:


public static void show(Long id) {
    Client client = Client.findById(id);
    render(id, client);   



You can only pass local variables in this way.

你只能传递 action 方法类的变量

Specify another template


If you don’t want to use the default template, you can specify your own template file during the render(…) call. Just pass it as the render method’s first parameter:


你可能不想使用默认的模板,那么可通过 render 方法来指定其他模板,例如:

public static void show(Long id) {
    Client client = Client.findById(id);
    render("Clients/showClient.html", id, client);   

Redirect to another URL

重定向到其他 URL 地址

The redirect(…) method emits a Redirect event that in turn generates an HTTP Redirect response.

Play 提供了一个 redirect 方法,可以将请求调整到其他的 URL 地址:

public static void index() {

Action chaining

Action 链

There is no equivalent to the Servlet API forward. An HTTP request can only invoke one action. If you need to invoke another action, you have to redirect the browser to the URL able to invoke that action. In this way, the browser URL is always consistent with the executed action, and the Back/Forward/Refresh management is much easier.

Action 链与 Servlet API 中的 forward 并不相同,一个 HTTP 请求只能调用一个 action 。如果你需要调用其他的 action ,你必须将浏览器重定向到你想要调用的 action 上。这样后退、前进和刷新操作就会简单很多。

You can send a Redirect response to any action, simply by invoking the action method in a Java way. The Java call is intercepted by the framework and the correct HTTP Redirect is generated.

For example:

你可以重定向到任意一个 action,只需要简单的调用该 action 方法而已,Play 框架会自动将这种形式的调用解析为 HTTP 的重定向。


public class Clients extends Controller {
    public static void show(Long id) {
        Client client = Client.findById(id);
    public static void create(String name) {
        Client client = new Client(name);

With these routes:

GET    /clients/{id}            Clients.show
POST   /clients                 Clients.create

    * The browser sends a POST to the /clients URL.
    * The Router invokes the Clients controller’s create action.
    * The action method call the show action method directly.
    * The Java call is intercepted and the Router reverse route generation creates the URL needed to invoke Clients.show with an id parameter.
    * The HTTP Response is 302 Location:/clients/3132.
    * The browser then issues GET /clients/3132.
    * …



A controller can define interception methods. Interceptors are invoked for all actions of the controller class and its descendants. It’s a useful way to define treatments that are common to all actions: verifying that a user is authenticated, loading request-scope information…

可以为控制器编写拦截方法,拦截器将在 action 执行的前后被调用。这个用来做用户权限的验证非常有用。

These methods have to be static but not public. You have to annotate these methods with a valid interception marker.

首先确保拦截器的方法不能定义为 public 但必须是 static 。然后可以通过 @Before 来声明这是一个拦截器方法。

Methods annotated with the @Before annotation are executed before each action call for this Controller.

被声明为 @Before 的方法将会在每个 action 方法被调用之前先执行。

So, to create a security check:


public class Admin extends Application {
    static void checkAuthentification() {
        if(session.get("user") == null) login();
    public static void index() {
        List<User> users = User.findAll();

If you don’t want the @Before method to intercept all action calls, you can specify a list of actions to exclude:

如果你不想拦截所有的 action 方法,那么可以指定不拦截哪些方法:

public class Admin extends Application {
    static void checkAuthentification() {
        if(session.get("user") == null) login();
    public static void index() {
        List<User> users = User.findAll();


Methods annotated with the @After annotation are executed after each action call for this Controller.

与 @Before 相反,@After 注释用来声明 action 结束调用的拦截器:

public class Admin extends Application {
    static void log() {
        Logger.info("Action executed ...");
    public static void index() {
        List<User> users = User.findAll();


Methods annotated with the @Finally annotation are executed after each action result is applied from for this Controller.

@Finally 拦截器声明用来定义一个方法在结果已经生成完成后执行:

public class Admin extends Application {
    static void log() {
        Logger.info("Response contains : " + response.out);
    public static void index() {
        List<User> users = User.findAll();

Controller hierarchy


If a Controller class is a subclass of another Controller class, interceptions are applied to the full Controller hierarchy.


Adding more interceptors using the @With annotation

可使用 @With 注释来增加更多的拦截器

Because Java does not allow multiple inheritance, it can be very limiting to rely on the Controller hierarchy to apply interceptors. But you can define some interceptors in a totally different class, and link them with any controller using the @With annotation.


因为 Java 不支持多重继承,这对控制器层次是限制很大的,但你可以通过定义一些拦截器,然后使用 @With 注释来声明。


public class Secure extends Controller {
    static void checkAuthenticated() {
        if(!session.containsKey("user")) {

And on another Controller:

public class Admin extends Application {

Session and Flash scope


If you have to keep data across multiple HTTP Requests, you can save them in the Session or the Flash scope. Data stored in the Session are available during the whole user session, and data stored in the flash scope are available to the next request only.

如果你需要在多个 HTTP 请求间保持数据,那么可以将数据保存在Session 或者是 Flash(闪屏)中。保存在 Session 中的数据在整个用户会话中都是有效的,而 Flash 就只对下一个请求有效。

It’s important to understand that Session and Flash data are not stored in the server but are added to each subsequent HTTP Request, using the Cookie mechanism. So the data size is very limited (up to 4Ko) and you can only store String values.

特别特别需要注意的时,与 J2EE 的做法不同的是,存放于 Session 和 Flash 的数据是通过 Cookie 保存在浏览器的,因此数据大小不能超过4k,而且只能存储字符串。


Of course, cookies are signed with a secret key so the client can’t modify the cookie data (or it will be invalidated). The Play session is not aimed to be used as a cache. If you need to cache some data related to a specific session, you can use the Play built-in cache mechanism and use the session.getId() key to keep them related to a specific user session.

当然了,cookies 是通过安全方式进行签名的,因此客户端是无法修改这个数据,只能使之无效。Play 框架的 Session 不能当作是缓存来使用,如果你需要为某个Session缓存某些数据,那么可以使用 session.getId() 来作为缓存的key。



public static void index() {
    List messages = Cache.get(session.getId() + "-messages", List.class);
    if(messages == null) {
        // Cache miss
        messages = Message.findByUser(session.get("user"));
        Cache.set(session.getId() + "-messages", messages, "30mn");

The cache has different semantics than the classic Servlet HTTP session object. You can’t assume that these objects will be always in the cache. So it forces you to handle the cache miss cases, and keeps your application fully stateless.

这个缓存在语义上跟 Servlet 的 HTTP Session 是不同的,你不能猜想说缓存中的数据是会一直存在的,因此你需要处理缓存数据失效的情况,使你的应用真正的无状态化。












这个貌似不错 我试用了一下






h1. Play framework overview


The Play framework is a clean alternative to bloated Enterprise Java stacks. It focuses on developer productivity and targets RESTful architectures. Play is a perfect companion to *agile software development*.

The Play framework’s goal is to ease web applications development while sticking with Java. Let’s see how this is possible.

p(note). **Wanna see some code?**

查看“你可以使用Play做的5件最酷的事”:5thinds,或者直接跳至“hello world 实例”:firstapp
Check "Five cool things you can do with play":5things, or start directly with the "hello world tutorial":firstapp.

h2. <a name="java">A Java framework without the pain</a>

Play is a pure Java framework and allows you to keep your preferred development tools and libraries. If you already use Java as a development platform you don’t need to switch to another language, another IDE and other libraries. *Just switch to a more productive Java environment!*

h2. <a name="reload">Fix the bug and hit Reload</a>

Java平台在开发效率方面已经是声名狼藉了,可能的原因就是重复 “编译-打包-部署”的循环。
The Java platform is infamous for its low productivity, mainly due to repeated and tedious compile-package-deploy cycles.

That’s why we rethought the development cycle to make developing with Play an efficient process.

The framework compiles your Java sources directly and hot-reloads them into the JVM without the need to restart the server. You can then edit, reload and see your modifications immediately, just as in a LAMP or Rails environment.

而且更有趣的是你按自己的爱好,仅仅使用一个简单的文本编辑器而避免使用全功能的Java IDE。
It’s so fun that you can even work with a simple text editor and skip a full-featured Java IDE if you want.


Whenever an error occurs, the framework makes its best effort to identify and show you the problem.


Even the stack traces are stripped down and optimized to make it easier to solve problems. Look how template execution is nicely integrated to the Java stack trace.


h2. <a>Simple stateless MVC architecture</a>

You’ve got a database on one side and a web browser on the other. Why should you have a state in between?

以有状态和组件式为基础的Java web框架使之容易的自动保存页面状态,但是这带来了很多其他问题,如果用户
Stateful and component based Java Web frameworks make it easy to automatically save page state, but that brings a lot of other problems: what happens if the user opens a second window? What if the user hits the browser back button?

PHP,Ruby on Rails和Django等许多web应用框架促进了“无共享”架构。
The "Share Nothing":http://zef.me/883/the-share-nothing-architecture architecture is promoted by many Web application frameworks from PHP to Ruby on Rails or Django. As the browser is becoming more and more powerful, it is now easy to use Ajax or offline storage to solve the state problems client-side.

我们不需要再去hack HTTP模型,在web上重建一个虚假的状态。另一方面,
We don’t need to hack the HTTP model anymore to rebuild a fake state over the Web. Another aspect of ‘share nothing’ is to make it easier to render portions of the page in parallel, and to do partial page updates (or progressive enhancements).

h2. <a name="http">HTTP-to-code mapping</a>

如果你已经使用了另外一种Java web框架,例如Servlet API或者Struts框架,你已经使用了把HTTP协议和Java API抽象的联系起来的奇怪的观念。(好难翻译啊)
我们不是这么想的。一个web应用框架应该给你完全的,直达的对Http进行操作,这是Play和其他Java web框架的一个根本不同。
If you’re already using another Java Web framework like the Servlet API or the Struts framework, you have already used an abstract view of the HTTP protocol with strange Java APIs and concepts. We think differently. A Web application framework should give you full, straightforward access to HTTP and its concepts. This is a fundamental difference between Play and other Java web application frameworks.

HTTP, the Request/Response pattern, the REST architectural style, content-type negotiation, URI are all major concepts for the Play framework.

例如,绑定一个URI 参数 到Java中调用就像下面一样
For instance, binding a URI pattern to a Java call is just one line:

bc. GET    /clients/{id}        Clients.show
If Ajax, REST and managing back/forward movement between web pages are some of the issues you face in your day-to-day web development, just give Play a try.

h2. <a name="templates">Efficient templating engine</a>

We like the idea behind JSP & Expression Language. But why do we need so many configuration files to create a tag library? Why can’t we have full access to the underlying object model? JSP has a lot of limitations and this is indeed frustrating. That’s why we’ve created a custom template system, inspired by JSP but without its constraints!

You and anyone else should be tired of writing things like this:

bc. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    <c:when test="${emails.unread != null && fn:size(emails.unread)}">
        You have ${fn:size(emails.unread)} unread email(s)!
        You have no unread emails!

We think you would certainly prefer to write:

bc. You have ${emails.unread ?: 'no'} ${emails.unread?.pluralize('email')} !
Play模板使用的 表达式语言是Groovy,它和Java语法一致,
Play使用这些模板系统区渲染HTML请求,你可以使用它去产生其他格式的文档例如 email信息,JSON输出等。
The expression language used by the play template engine is "Groovy":http://groovy.codehaus.org/, which provides a syntax consistent with Java. While Play mainly uses the templating system to render HTML responses, you are free to use it to generate any other documents such as e-mail messages, JSON responses, etc.

h2. <a>JPA on steroids</a>

The Java Persistence API (JPA) is the cleanest object-relational mapping ORM) API available for Java. If you already know it you will be amazed how much simpler it becomes with Play. With nothing to configure, Play will automatically start the JPA Entity Manager and magically synchronize it while the code is reloaded.

而且如果你使用提供的 **play.db.jpa.Model** 超类时,它会帮助你 把代码变的更漂亮。来看一下。
Moreover if you use the provided **play.db.jpa.Model** superclass it will help make your code prettier. Have a look:

bc. public void messages(int page) {
    User connectedUser = User.find("byEmail", connected());
    List<Message> messages = Message.find(
        "user = ? and read = false order by date desc",
    ).from(page * 10).fetch(10);
    render(connectedUser, messages);

h2. <a>Test driven development (if you like it)</a>

的acceptance 测试,然后直接在浏览器中使用Selenium运行。代码覆盖率也会考虑到。
The integrated test runner makes it easy for you do test-driven development (TDD). You can write all kinds of tests, from simple unit tests to full acceptance tests, and run them directly in a browser using "Selenium":http://seleniumhq.org/. Code coverage is also measured.


h2. <a name="full-stack">Full-stack application framework</a>

Play框架的灵感来自我们自己的Java应用,它包含了创建现代 web应用所需的一切。它包含:
使用hibernate管理对象关系映射(使用了JPA API)
使用JSON和XML的简单web service(我们说的是’真正‘的web service,不是SOAP)
你的web 应用可以部署到任何支持的地方(应用服务器,GAE,云等)

The Play framework was initially inspired by our own Java applications. It has all the tools needed to create a modern web application, including:

* relational database support through JDBC.
* object-relational mapping using Hibernate (with the JPA API).
* integrated cache support, with easy use of the distributed memcached system if needed.
* straightforward web services consumption either in JSON or XML (we are talking *real* web services here; not the SOAP stuff).
* OpenID support for distributed authentication.
* your web application ready to be deployed anywhere (application server, Google App Engine, Cloud, etc…)
* image manipulation API.

Play模块化的结构使你可以把web 应用和其他结合在一起,感谢模块,你可以以一种非常简单的方式重用你的Java 代码,
The modular architecture lets you combine a web application with many others. Thanks to "application modules":modules, you can reuse your Java code, templates and static resources (such as JavaScript and CSS files) in a simple way.

p(note). **Give it a try**
"Install the Play framework":install and start developing your first application.







  1. public static void fileupload(File attachment){  
  2.     if(request.method.equalsIgnoreCase("GET")){  
  3.         render();  
  4.     }else{  
  5.         Files.copy(attachment, Play.getFile("public/attachment/"+attachment.getName()));  
  6.         render();  
  7.     }  
  8. } //文件上传的一段