1. 简介

在上一篇 RESTEasy 教程 中,我们主要关注了 JAX-RS 2.0 的服务端实现。

JAX-RS 2.0 同时也引入了一个新的客户端 API,允许你调用远程的 RESTful Web 服务。Jersey、Apache CXF、Restlet 和 RESTEasy 都是目前比较主流的实现方案之一。

本文将带你了解如何通过 RESTEasy 提供的客户端 API 来消费 REST 接口。

2. 项目依赖配置

在你的 pom.xml 文件中添加以下依赖项:

<properties>
    <resteasy.version>4.7.2.Final</resteasy.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-client</artifactId>
        <version>${resteasy.version}</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    ...
</dependencies>

3. 客户端核心类

RESTEasy 客户端的核心由三个类构成:

  • Client
  • WebTarget
  • Response

其中:

  • Client 是构建 WebTarget 实例的工厂。
  • WebTarget 表示一个具体的 URL 或 URL 模板,你可以基于它继续构建子资源路径或发起请求。

创建 Client 有两种方式:

  • 使用标准的 org.jboss.resteasy.client.ClientRequest
  • 使用 RESTEasy Proxy Framework(推荐):通过 ResteasyClientBuilder 构建

我们将重点介绍第二种方式 —— Proxy 模式

Proxy 框架并不会像服务端那样处理请求,而是帮你构建 HTTP 请求并发送到远程服务

所以我们可以直接写一个 Java 接口,并使用 JAX-RS 的注解来描述请求行为。

3.1. ServicesClient 接口定义

@Path("/movies")
public interface ServicesInterface {

    @GET
    @Path("/getinfo")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    Movie movieByImdbId(@QueryParam("imdbId") String imdbId);

    @POST
    @Path("/addmovie")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    Response addMovie(Movie movie);

    @PUT
    @Path("/updatemovie")
    @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    Response updateMovie(Movie movie);

    @DELETE
    @Path("/deletemovie")
    Response deleteMovie(@QueryParam("imdbId") String imdbId);
}

3.2. Movie 数据模型

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "movie", propOrder = { "imdbId", "title" })
public class Movie {

    protected String imdbId;
    protected String title;

    // getters and setters
}

3.3. 发起请求

下面是使用 Proxy 创建客户端并调用接口的完整示例:

String transformerImdbId = "tt0418279";
Movie transformerMovie = new Movie("tt0418279", "Transformer 2");
UriBuilder FULL_PATH = UriBuilder.fromPath("http://127.0.0.1:8082/resteasy/rest");
 
ResteasyClient client = (ResteasyClient)ClientBuilder.newClient();
ResteasyWebTarget target = client.target(FULL_PATH);
ServicesInterface proxy = target.proxy(ServicesInterface.class);

// POST
Response moviesResponse = proxy.addMovie(transformerMovie);
System.out.println("HTTP code: " + moviesResponse.getStatus());
moviesResponse.close();

// GET
Movie movies = proxy.movieByImdbId(transformerImdbId);

// PUT
transformerMovie.setTitle("Transformer 4");
moviesResponse = proxy.updateMovie(transformerMovie);
moviesResponse.close();

// DELETE
moviesResponse = proxy.deleteMovie(batmanMovie.getImdbId());
moviesResponse.close();

⚠️ 注意事项:

  • RESTEasy 客户端底层使用的是 Apache HttpClient。
  • 所有 Response 对象必须手动调用 .close() 方法释放连接,否则后续请求会抛异常。
  • DTO(如 Movie)的序列化/反序列化工作由 JAXB 或 Jackson 自动完成,前提是类已正确标注。

3.4. 使用连接池优化并发性能

前面的例子中,默认只允许一个连接。如果你连续发起多个请求而不关闭响应,就会报错:

java.lang.IllegalStateException:
Invalid use of BasicClientConnManager: connection still allocated.
Make sure to release the connection before allocating another one.

这是因为默认使用的 HttpClient 是 SingleClientConnManager,只能维持单个连接。

为了解决这个问题,我们需要启用连接池:

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
cm.setMaxTotal(200); // 最大连接数
cm.setDefaultMaxPerRoute(20); // 每个路由最大连接数
ApacheHttpClient43Engine engine = new ApacheHttpClient43Engine(httpClient);

ResteasyClient client = ((ResteasyClientBuilder) ClientBuilder.newBuilder()).httpEngine(engine).build();
ResteasyWebTarget target = client.target(FULL_PATH);
ServicesInterface proxy = target.proxy(ServicesInterface.class);

✅ 这样就可以支持多个并发请求,避免频繁释放连接带来的麻烦。

4. 总结

这篇文章带你快速入门了 RESTEasy Proxy Framework,并通过简单的代码展示了如何封装一个 REST 客户端。

Proxy 框架本质上是 JAX-RS 服务端规范的一个“镜像”版本,它把服务端的声明式接口变成了客户端的调用入口。

文中示例项目可以在 GitHub 上找到:sample project on GitHub


原始标题:RESTEasy Client API