拥抱Restful--Jersey框架

主要通过另一个轻量级的框架-- `Jersey` 整合 Spring 来构建一个 Restful 服务

Posted by maybelence on 2021-04-19

引言

提到 RestFul ,很容易联想到 Spring 框架,如果你在此之前没有接触过 Spring 框架,建议先点击 传送门 了解一下如何通过 Spring 快速构建一个 Restful Web 服务。

今天我们主要通过另一个轻量级的框架— Jersey 整合 Spring 来构建一个 Restful 服务。 Jersey 是一个用于构建 Restful Web 服务的一个开源框架。是 JAX-RS 规范的一种具体实现。

Maven 依赖

首先引入 Jersey 相关的依赖:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>3.0.1</version>
</dependency>

如果要整合 Spring 框架的话,我们还需要引入 jersey-spring4 依赖:

1
2
3
4
5
6
<!-- 整合spring -->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring4</artifactId>
<version>3.0.0-M6</version>
</dependency>

Web 配置

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

1
2
3
4
5
6
7
8
9
10
11
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationInitializer implements WebApplicationInitializer {

public void onStartup(javax.servlet.ServletContext servletContext) {
AnnotationConfigWebApplicationContext context
= new AnnotationConfigWebApplicationContext();

servletContext.addListener(new ContextLoaderListener(context));
servletContext.setInitParameter("contextConfigLocation", "com.example.server");
}
}

这里添加 @Order(Ordered.HIGHEST_PRECEDENCE) 注解的作用是确保 WebApplicationInitializer 在 Jersey-Spring 默认初始化程序之前执行。

使用 Jersey JAX-RS 服务

资源表示类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@XmlRootElement
public class Employee {
private int id;
private String firstName;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}
}

当仅需要 XML 支持(除了JSON)时,才需要引入 @XmlRootElement 这一类的 JAXB 注解,

实现服务

现在我们通过 JAX-RS 注解来构建一个 RESTful Web 服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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 注解表示服务请求路由,我们也可以将变量嵌入到请求路由中,如代码中的 {id} 。如果需要获取该变量值,可以通过 @PathParam 注解。

@GET@PUT@POST , @DELETE@HEAD 标识请求 HTTP 的方式。

@Produces 表示响应的数据类型,在示例中,我们已将其配置为根据HTTP请求头为 (Acceptapplication/json 或 application/xml) 的值返回JSON或XML。

@Consumes 定义服务可用的 MIME 类型,具体的数据类型取决于请求头 Content-Type(application/json或application/xml)。

@Context 注解用于将信息注入类字段,bean属性或方法参数。在示例中,我们使用它来注入 UriInfo 。我们还可以使用它来注入 ServletConfig , ServletContext , HttpServletRequest 和 HttpServletResponse 。

使用 ExceptionMapper

ExceptionMapper 可以帮助我们拦截异常并将适当的HTTP响应代码返回给客户端。在以下示例中,如果抛出 EmployeeNotFound 异常,则返回 HTTP 响应代码 404 :

1
2
3
4
5
6
7
8
 @Provider
public class NotFoundExceptionHandler
implements ExceptionMapper<EmployeeNotFound> {

public Response toResponse(EmployeeNotFound ex) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}

管理资源类

最后,让我们根据应用程序路径连接所有服务实现类和异常映射:

1
2
3
4
5
6
7
8
9
10
@ApplicationPath("/resources")
public class RestConfig extends Application {
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(
Arrays.asList(
EmployeeResource.class,
NotFoundExceptionHandler.class,
AlreadyExistsExceptionHandler.class));
}
}

API 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}
}

总结

本文介绍了 Jersey 框架并开发了一个简单的 API 。我们引入 Spring 用于依赖关系注入功能。还看到了 ExceptionMapper 的使用。


Copyright by @maybelence.

...

...

00:00
00:00