1. 概述
REST 架构风格已经流行多年,至今仍备受关注。在 Java 中实现 RESTful API 有多种方式:可以使用 Spring、JAX-RS,或者如果你足够自信,甚至可以直接编写原生 Servlet。核心在于暴露 HTTP 方法的能力,其余都是如何组织这些方法以及如何引导客户端调用你的 API。
正如标题所示,本文将聚焦 JAX-RS。但“只是一个 API”是什么意思?这意味着本文旨在澄清 JAX-RS 与其实现之间的混淆,并展示一个规范的 JAX-RS Web 应用示例。
2. 在 Java EE 中的定位
JAX-RS 本质上只是一个规范——由 Java EE 提供的一组接口和注解。当然,我们还有具体实现,其中最知名的是 RESTEasy 和 Jersey。
如果你决定构建一个符合 JEE 标准的应用服务器,Oracle 会要求你的服务器必须为部署的应用提供 JAX-RS 实现。这就是为什么它被称为 Java Enterprise Edition 平台。
另一个规范与实现的典型例子是 JPA 和 Hibernate。
2.1. 轻量级 War 包
这对我们开发者有什么帮助?帮助在于我们的部署包应该尽可能精简,让应用服务器提供所需库。开发 RESTful API 时同样适用:最终产物不应包含任何 JAX-RS 实现细节。
✅ 正确做法:依赖服务器提供的实现
❌ 错误做法:将实现库打包进 War
当然,我们可以自行提供实现(这里 有 RESTEasy 教程)。但这样就不能再称我们的应用为“Java EE 应用”了。如果某天有人说:“该切换到 Glassfish 或 Payara 了,JBoss 太贵了!”,虽然理论上可行,但过程会非常痛苦。
自行提供实现时,必须确保服务器排除其内置实现——通常需要在部署包中添加专有 XML 文件。这种文件往往充斥着各种无人理解的标签和指令,除了三年前离职的开发者。
2.2. 始终了解你的服务器
我们强调要善用平台提供的资源。在选择服务器前,至少在生产环境中,应该了解它提供的 JAX-RS 实现细节(名称、厂商、版本和已知缺陷)。例如:
- Glassfish 内置 Jersey
- Wildfly/JBoss 内置 RESTEasy
这需要前期投入研究时间,但只需在项目启动或迁移时做一次。
3. 实战示例
想快速上手 JAX-RS?最简单的方式是创建 Maven Web 项目,在 pom.xml 添加以下依赖:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
我们选择 JavaEE 7,因为已有大量应用服务器支持它。这个 API jar 包含了所需的注解(位于 javax.ws.rs 包)。为什么 scope 是 "provided"?因为编译时需要它,但运行时由服务器提供。
添加依赖后,首先编写入口类:一个继承 javax.ws.rs.core.Application 的空类,并用 javax.ws.rs.ApplicationPath 注解:
@ApplicationPath("/api")
public class RestApplication extends Application {
}
我们定义了入口路径 /api,所有资源路径都会以此作为前缀。
接下来看资源类示例:
@Path("/notifications")
public class NotificationsResource {
@GET
@Path("/ping")
public Response ping() {
return Response.ok().entity("Service online").build();
}
@GET
@Path("/get/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getNotification(@PathParam("id") int id) {
return Response.ok()
.entity(new Notification(id, "john", "test notification"))
.build();
}
@POST
@Path("/post/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response postNotification(Notification notification) {
return Response.status(201).entity(notification).build();
}
}
包含三个接口:
- 简单的 ping 接口用于检测服务状态
- GET 接口获取 Notification(普通 POJO,含属性和 getter/setter)
- POST 接口创建 Notification
将此 War 部署到任何支持 JEE7 的应用服务器,以下命令将生效:
curl http://localhost:8080/simple-jaxrs-ex/api/notifications/ping/
curl http://localhost:8080/simple-jaxrs-ex/api/notifications/get/1
curl -X POST -d '{"id":23,"text":"lorem ipsum","username":"johana"}'
http://localhost:8080/simple-jaxrs-ex/api/notifications/post/
--header "Content-Type:application/json"
其中 simple-jaxrs-ex 是 Web 应用的上下文根。
⚠️ 踩坑提示:此代码在 Glassfish 4.1.0 和 Wildfly 9.0.1.Final 测试通过。但后两个命令在 Glassfish 4.1.1 会失败,因为这个 bug。该版本存在 JSON 序列化已知问题(若必须使用此版本,需自行处理 JSON 编组)。
4. 总结
记住关键点:JAX-RS 是强大的 API,你需要的绝大多数功能(甚至全部)已由 Web 服务器实现。无需把部署包变成难以管理的库堆。
本文展示了简单示例,实际场景可能更复杂。比如你可能需要自定义编组器。遇到这种情况,优先寻找基于 JAX-RS 的解决方案,而非 Jersey、RESTEasy 等具体实现。很可能只需一两个注解就能解决问题。