带有 Jersey 和 Jetty 的独立 Java 应用程序

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

介绍

我构建了一个运行独立 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 应用程序中运行该服务的详细信息。