介绍
我构建了一个运行独立 Java 应用程序的 小示例 ,该应用程序既提供静态 HTML、JavaScript、CSS 内容,又发布 REST Web 服务。该示例使用 Jersey 和 Jetty。这个示例可能值得发表几篇文章,以便有足够的时间来解释各个部分如何组合在一起,因此我将从构成 JAX-RS 应用程序的主要 Java 部分开始。
几年前,当我通过一些幻灯片来教授 Java 课程(主要是 Java EE 和 Spring)时,我使用 Spring WebMVC 创建了一个 REST Web 服务。我写了一些关于它如何工作的帖子(从 这里 开始)。
我最近再次教授该课程,更新到更高版本的 Java 和更高版本的库。当我接触到 Spring WebMVC 课程时,我想教它,因为我仍然认为它对于使用 Spring 框架的应用程序来说是一个不错的选择(因为它与 Spring 依赖注入集成)。但是现在我们当然可以选择 Java API for RESTful Web Services (JAX-RS)。
所以我将前面的示例改编为 JAX-RS。幸运的是,Spring WebMVC 应用程序使用的一些小技巧仍然适用。
提供商类
JAX-RS 将 REST Web 服务的工作分配给一个或多个提供者类。每个提供者类处理整个应用程序中的一组路径,每个提供者类可以有多个处理特定路径和 HTTP 方法的方法。
提供者类是普通的旧 Java 对象 (POJO),使用注释来指定 JAX-RS 参数。这是此应用程序的提供程序类。
@Path("/calculator")
public class Calculator {
@GET
@Path("/calc/{op}/{left}/{right}")
public Calculation calculate(@PathParam("op") String op, @PathParam("left") Integer left,
@PathParam("right") Integer right) {
Calculation result = new Calculation();
result.setOperation(op);
result.setLeft(left);
result.setRight(right);
return doCalc(result);
}
@POST
@Path("/calc2")
public Calculation calculate(Calculation calc) {
return doCalc(calc);
}
private Calculation doCalc(Calculation c) {
String op = c.getOperation();
int left = c.getLeft();
int right = c.getRight();
if (op.equalsIgnoreCase("subtract")) {
c.setResult(left - right);
} else if (op.equalsIgnoreCase("multiply")) {
c.setResult(left * right);
} else if (op.equalsIgnoreCase("divide")) {
c.setResult(left / right);
} else {
c.setResult(left + right);
}
return c;
}
}
该示例说明了
GET
和
POST
HTTP 方法,并说明了通过 URL 组件或通过请求主体传递参数。
上面使用的关键注释是:
-
@Path
:添加用于匹配传入 URL 的路径组件。路径组件是累积的,因此类注解和方法注解一起工作。该路径可以包含用于提供参数的模板。 -
@GET
:指定处理 HTTP GET 请求的方法。 -
@POST
:指定处理 HTTP POST 请求的方法。 -
@PathParam
:将 URL 中的模板与方法参数相关联。
此示例中未说明的是
@QueryParam
,其工作方式与
@PathParam
类似,但匹配表单输入(在 URL 中编码为
?name1=value1&name2=value2
对,或在请求正文中带有换行符的
name=value
列表中) .
对于那些熟悉 Spring WebMVC 的人,请注意注解非常相似。另请注意,某些注释(如
@RequestBody
和
@ResponseBody
指定请求主体应转换为参数,或者返回的 Java 对象应成为响应主体)是假定的而不是指定的。
JAX-RS 应用程序
JAX-RS 应用程序类允许自定义为提供程序扫描哪些包。
@Path("/calculator")
public class Calculator {
@GET
@Path("/calc/{op}/{left}/{right}")
public Calculation calculate(@PathParam("op") String op, @PathParam("left") Integer left,
@PathParam("right") Integer right) {
Calculation result = new Calculation();
result.setOperation(op);
result.setLeft(left);
result.setRight(right);
return doCalc(result);
}
@POST
@Path("/calc2")
public Calculation calculate(Calculation calc) {
return doCalc(calc);
}
private Calculation doCalc(Calculation c) {
String op = c.getOperation();
int left = c.getLeft();
int right = c.getRight();
if (op.equalsIgnoreCase("subtract")) {
c.setResult(left - right);
} else if (op.equalsIgnoreCase("multiply")) {
c.setResult(left * right);
} else if (op.equalsIgnoreCase("divide")) {
c.setResult(left / right);
} else {
c.setResult(left + right);
}
return c;
}
}
ResourceConfig
是一个 Jersey 类,它从标准的 JAX-RS
Application
类扩展而来,并提供包扫描和其他辅助应用程序。它还为我们必须自己提供的标准
getClasses()
和
getSingletons()
方法提供实现。
网站.xml
虽然 Servlet 3.0 可以在不使用部署描述符的情况下部署应用程序,但在使用 Maven Jetty 插件运行时,它可以更轻松地将 Jetty 与 Jersey 连接起来。
@Path("/calculator")
public class Calculator {
@GET
@Path("/calc/{op}/{left}/{right}")
public Calculation calculate(@PathParam("op") String op, @PathParam("left") Integer left,
@PathParam("right") Integer right) {
Calculation result = new Calculation();
result.setOperation(op);
result.setLeft(left);
result.setRight(right);
return doCalc(result);
}
@POST
@Path("/calc2")
public Calculation calculate(Calculation calc) {
return doCalc(calc);
}
private Calculation doCalc(Calculation c) {
String op = c.getOperation();
int left = c.getLeft();
int right = c.getRight();
if (op.equalsIgnoreCase("subtract")) {
c.setResult(left - right);
} else if (op.equalsIgnoreCase("multiply")) {
c.setResult(left * right);
} else if (op.equalsIgnoreCase("divide")) {
c.setResult(left / right);
} else {
c.setResult(left + right);
}
return c;
}
}
此配置使用 Jersey 提供的 servlet 委托给我们之前定义的 JAX-RS 应用程序类。这也允许我们为 JAX-RS 服务指定路径的第一个组件(使它们区别于我们也希望提供的静态文件)。
部署和运行
Maven POM 文件处理构建 JAR(用于独立应用程序,在下一篇文章中讨论)和 WAR(用于部署到 Servlet 容器)。它还包括 Maven Jetty 插件,允许我们使用
mvn jetty:run
从命令行运行应用程序。
客户要求
该示例包括 JavaScript 和 Java 客户端,我将在另一篇文章中对此进行讨论。当然可以使用任何 Web 客户端,但请注意,此 REST 服务在响应客户端时非常谨慎。
我们列出了 Jackson 对 Jersey 插件的依赖,因此我们可以在 Java 和 JSON 之间移动。但是,客户端必须指定标头
Accept: application/json
;否则,服务器默认为 XML。此外,当为 POST 请求向服务器提供数据时,客户端还必须指定
Content-Type: application/json
标头。
下一步
下一篇文章将提供有关使用嵌入式 Jetty 服务器在常规 Java 应用程序中运行该服务的详细信息。