Play Framework 综合教程
Play Framework 是一个用于构建现代、可扩展、高性能 Web 应用的开源框架,它以其“无状态”、“非阻塞”和“约定优于配置”的理念而闻名,非常适合构建 RESTful API 和实时应用。

第一部分:入门准备
什么是 Play Framework?
- 核心特点:
- 无状态: 服务器不存储客户端的会话状态,这使得应用更容易水平扩展,会话数据通常存储在客户端(加密 Cookie)或外部存储(如 Redis)中。
- 非阻塞 & 异步: 基于 Akka 和 Reactive Streams,Play 能够高效处理大量并发连接,非常适合 I/O 密集型应用。
- 约定优于配置: 遵循一套清晰的目录结构和命名约定,让你能专注于业务逻辑,而不是繁琐的配置。
- 实时能力: 内置 WebSocket 和 Server-Sent Events (SSE) 支持,便于构建实时功能(如聊天室、仪表盘)。
- 热重载: 开发时,代码修改后应用会自动重启,极大地提升了开发效率。
- 多语言支持: 官方支持 Java 和 Scala。
环境准备
在开始之前,请确保你的系统已安装以下工具:
- JDK 17 或更高版本: Play 3.x 需要 Java 17+,你可以从 Adoptium (Eclipse Temurin) 下载。
- Build 工具 (二选一):
- IDE (推荐):
- IntelliJ IDEA: 对 Play 和 Scala/Java 的支持最好。
- VS Code: 配合合适的插件(如 Java Extension Pack)也能有不错的体验。
第二部分:创建你的第一个 Play 项目
我们将使用 Giter8 命令行工具来快速生成一个标准的 Play 项目骨架。
安装 Giter8
如果你没有安装 Giter8,可以通过以下命令安装(需要先安装 coursier):
cs install g8
创建项目
打开终端,运行以下命令,它会从官方模板创建一个新项目。

g8 playframework/play-java-seed.g8
命令会提示你输入一些信息:
organization_name(e.g.,com.example)project_name(e.g.,my-first-play-app)package_name(e.g.,com.example)play_version(e.g.,x.x)
输入完毕后,按回车,Giter8 会在当前目录下创建一个以你的项目名命名的文件夹。
进入项目并运行
cd my-first-play-app ./gradlew run
./gradlew是 Gradle 的 Wrapper,无需全局安装 Gradle。run命令会启动 Play 应用。
首次运行时,Gradle 会下载所需的依赖,这可能需要几分钟,完成后,你会看到类似以下的输出:
--- (Running the application from /path/to/your/project, using CWD as base directory) ---
Listening for transport dt_socket at address: 5005
[info] p.c.s.AkkaHttpServer - Listening on address /0.0.0.0:9000
打开你的浏览器,访问 http://localhost:9000,你应该能看到 Play 的欢迎页面。

体验热重载
保持应用运行,打开 app/controllers/HomeController.java 文件,修改其中的消息,
public Result index(Http.Request request) {
return ok("Hello, my first Play app! This is a change."); // 修改这里的文本
}
保存文件,你会看到控制台应用自动重启,刷新浏览器,你会看到更新后的消息,这就是 Play 的热重载功能!
第三部分:核心概念详解
一个 Play 应用的核心结构如下:
my-first-play-app/
├── app/
│ ├── controllers/ # 控制器:处理 HTTP 请求
│ ├── models/ # 模型:数据结构(可选,如 JPA 实体)
│ ├── views/ # 视图:渲染 HTML 页面(仅用于 Web 应用)
│ └── Application.java # 应用入口点
├── conf/
│ ├── application.conf # 应用配置文件
│ └── routes # 路由定义文件
├── build.gradle # Gradle 构建文件
└── public/ # 静态资源 (CSS, JS, 图片等)
路由
conf/routes 文件是 Play 的“交通警察”,它定义了 URL 如何映射到控制器的方法。
格式: HTTP_METHOD /path/to/controller -> controllers.ControllerName.actionMethod
示例 (conf/routes):
# 首页 GET / controllers.HomeController.index # 一个带路径参数的页面,/hello/World GET /hello/:name controllers.HomeController.sayHello(name: String) # 一个带查询参数的页面,/items?id=123 GET /items controllers.HomeController.getItem(id: Long)
name是一个路径参数,它会作为参数传递给控制器方法。id: Long是一个查询参数,Play 会自动尝试将id的值转换为Long类型。
控制器
控制器是业务逻辑的核心,它接收 HTTP 请求,进行处理,并返回一个 Result 对象。Result 是对 HTTP 响应的封装,可以是 HTML、JSON、重定向等。
示例 (app/controllers/HomeController.java):
package controllers;
import play.mvc.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class HomeController extends Controller {
// 同步方法,返回简单的 HTML 或文本
public Result index(Http.Request request) {
// request 对象包含了所有关于 HTTP 请求的信息
return ok("Hello, my first Play app!");
}
// 带路径参数的方法
public Result sayHello(String name) {
return ok("Hello, " + name + "!");
}
// 返回 JSON 数据 (现代 API 的标准做法)
public Result getItem(Long id) {
// 在真实应用中,这里会从数据库查询数据
if (id == 123) {
return ok("{\"id\": 123, \"name\": \"Sample Item\"}");
} else {
return notFound("Item not found");
}
}
// 异步方法,返回 CompletionStage<Result>
// 这对于调用外部服务或数据库等耗时操作至关重要
public CompletionStage<Result> asyncGetItem(Long id) {
return CompletableFuture.supplyAsync(() -> {
// 模拟一个耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "{\"id\": " + id + ", \"name\": \"Async Item\"}";
}).thenApply(itemJson -> ok(itemJson));
}
}
Result.ok(): 返回一个 200 OK 响应。Result.notFound(): 返回一个 404 Not Found 响应。CompletionStage<Result>: 这是实现非阻塞异步的关键,Play 的底层是异步的,让你的控制器方法返回CompletionStage可以充分利用这一点。
视图
视图用于生成 HTML 响应,Play 使用自己的模板引擎,它强大且易于使用。
示例 (app/views/index.scala.html):
@(name: String) @* 定义模板接收一个名为 name 的参数 *@
<!DOCTYPE html>
<html>
<head>
<title>Play Framework</title>
</head>
<body>
<h1>Hello @name!</h1> @* 使用 @ 符号来嵌入变量或执行代码 *@
<p>This is a rendered view from Play.</p>
</body>
</html>
修改控制器以使用视图:
public Result index(Http.Request request) {
return ok(views.html.renderedPage.render("World")); // 调用视图并传入参数
}
视图文件 (`app/views/renderedPage.scala
