1. 概述

Jersey 是一个开源框架,用于开发 RESTful Web 服务。它是 JAX-RS 的参考实现。

在这篇文章中,我们将探讨如何使用 Jersey 2 创建 RESTful Web 服务,并结合 Spring 的依赖注入(DI)和 Java 配置。

2. Maven 依赖

首先,让我们在 pom.xml 中添加依赖:

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.26</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.26</version>
</dependency>

为了集成 Spring,还需要添加 jersey-spring4 的依赖:

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring4</artifactId>
    <version>2.26</version>
</dependency>

这些依赖的最新版本可以在 jersey-container-servletjersey-media-json-jacksonjersey-spring4 查找。

3. Web 配置

接下来,我们需要设置一个 Web 项目进行 Servlet 配置。为此,我们将使用 Spring 的 WebApplicationInitializer

@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationInitializer 
  implements WebApplicationInitializer {
 
    @Override
    public void onStartup(ServletContext servletContext) 
      throws ServletException {
 
        AnnotationConfigWebApplicationContext context 
          = new AnnotationConfigWebApplicationContext();
 
        servletContext.addListener(new ContextLoaderListener(context));
        servletContext.setInitParameter(
          "contextConfigLocation", "com.baeldung.server");
    }
}

在这里,我们添加了 @Order(Ordered.HIGHEST_PRECEDENCE) 注解,以确保我们的初始化器在 Jersey-Spring 的默认初始化器执行之前运行。

4. 使用 Jersey JAX-RS 的服务

4.1. 资源表示类

现在,我们使用一个示例资源表示类:

@XmlRootElement
public class Employee {
    private int id;
    private String firstName;

    // standard getters and setters
}

请注意,如果需要 XML 支持(除了 JSON 之外),则需要像 @XmlRootElement 这样的 JAXB 注解。

4.2. 服务实现

接下来,我们将看看如何使用 JAX-RS 注解创建 RESTful Web 服务:

@Path("/employees")
public class EmployeeResource {
 
    @Autowired
    private EmployeeRepository employeeRepository;

    @GET
    @Path("/{id}")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Employee getEmployee(@PathParam("id") int id) {
        return employeeRepository.getEmployee(id);
    }

    @POST
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public Response addEmployee(
      Employee employee, @Context UriInfo uriInfo) {
 
        employeeRepository.addEmployee(new Employee(employee.getId(), 
          employee.getFirstName(), employee.getLastName(), 
          employee.getAge()));
 
        return Response.status(Response.Status.CREATED.getStatusCode())
          .header(
            "Location", 
            String.format("%s/%s",uriInfo.getAbsolutePath().toString(), 
            employee.getId())).build();
    }
}

@Path 注解提供了服务的相对 URI 路径。URI 语法中可以嵌入变量,如 {id},这些变量将在运行时替换。要获取变量的值,我们可以使用 @PathParam 注解。

@GET, @PUT, @POST, @DELETE@HEAD 定义请求的 HTTP 方法,这些方法将由带有注解的方法处理。

@Produces 注解定义了端点的响应类型(MIME 媒体类型)。在我们的例子中,根据 HTTP 头部 Acceptapplication/jsonapplication/xml)配置了返回 JSON 或 XML。

另一方面,@Consumes 注解定义了服务可以消费的 MIME 媒体类型。在本例中,根据 HTTP 头部 Content-Typeapplication/jsonapplication/xml),服务可以消费 JSON 或 XML。

@Context 注解用于将信息注入到类字段、bean 属性或方法参数中。在我们的示例中,我们使用它注入 UriInfo。此外,还可以注入 ServletConfigServletContextHttpServletRequestHttpServletResponse

5. 使用 ExceptionMapper

ExceptionMapper 允许我们捕获异常并向客户端返回适当的 HTTP 响应代码。例如,如果抛出 EmployeeNotFound 异常,将返回 HTTP 状态码 404:

@Provider
public class NotFoundExceptionHandler 
  implements ExceptionMapper<EmployeeNotFound> {
 
    public Response toResponse(EmployeeNotFound ex) {
        return Response.status(Response.Status.NOT_FOUND).build();
    }
}

6. 管理资源类

最后,我们将所有服务实现类和异常映射器与应用程序路径连接起来:

@ApplicationPath("/resources")
public class RestConfig extends Application {
    public Set<Class<?>> getClasses() {
        return new HashSet<Class<?>>(
          Arrays.asList(
            EmployeeResource.class, 
            NotFoundExceptionHandler.class, 
            AlreadyExistsExceptionHandler.class));
    }
}

7. API 测试

现在,让我们使用实际测试来测试 API:

public class JerseyApiLiveTest {

    private static final String SERVICE_URL
      = "http://localhost:8082/spring-jersey/resources/employees";

    @Test
    public void givenGetAllEmployees_whenCorrectRequest_thenResponseCodeSuccess() 
      throws ClientProtocolException, IOException {
 
        HttpUriRequest request = new HttpGet(SERVICE_URL);

        HttpResponse httpResponse = HttpClientBuilder
          .create()
          .build()
          .execute(request);

        assertEquals(httpResponse
          .getStatusLine()
          .getStatusCode(), HttpStatus.SC_OK);
    }
}

8. 总结

在这篇文章中,我们介绍了 Jersey 框架并创建了一个简单的 API。我们利用了 Spring 的依赖注入功能。还展示了如何使用 ExceptionMapper

如往常一样,完整的源代码可以在 这个 Github 项目 中找到。