【JAVA】JAVA中对接外部接口的常用方法汇总

【JAVA】JAVA中对接外部接口的常用方法汇总

前言

在现代的分布式系统和微服务架构中,Java应用程序经常需要与外部服务进行交互,调用其提供的API接口以获取数据或执行操作。这些接口可能遵循不同的设计风格和协议,例如常见的RESTful API、基于XML的SOAP服务,以及日益流行的GraphQL查询语言(通常也通过HTTP传输)。了解不同接口类型的特点对于选择合适的对接方法至关重要,您可以参考笔者的另一篇笔记【接口】接口类型总览与对比来获取更详细的信息。

无论是调用第三方服务(如支付接口、天气API、社交媒体API),还是内部不同微服务之间的通信,高效、稳定地对接外部接口是保证系统功能正常运行的关键。本文旨在介绍几种在Java中常用的对接外部HTTP接口的方法,重点讨论它们在处理不同类型接口(特别是RESTful API)时的特点、适用场景,并提供基本的使用示例,希望能帮助开发者根据实际需求选择最合适的工具。

一、HttpURLConnection

HttpURLConnection是Java标准库(java.net包)中提供的用于进行HTTP通信的基础类。它是JDK自带的,无需引入任何第三方依赖即可使用。

(一)特点

  1. 原生支持:作为JDK的一部分,具有良好的兼容性和普适性。
  2. 简单直接:对于简单的GET、POST请求,使用起来相对直接。
  3. 功能基础:功能相对基础,缺少连接池、复杂的请求配置、自动重试等高级特性。
  4. 性能一般:相比专门的HTTP客户端库,性能和效率可能较低,尤其在高并发场景下。 1

(二)使用示例

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpURLConnectionExample {

public static void main(String[] args) {
try {
// 发送GET请求
sendGetRequest("https://api.example.com/data");

// 发送POST请求
String postData = "{\"name\":\"John Doe\", \"age\":30}";
sendPostRequest("https://api.example.com/users", postData);
} catch (Exception e) {
e.printStackTrace();
}
}

// 发送GET请求
private static void sendGetRequest(String urlString) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// 设置请求方法为GET
connection.setRequestMethod("GET");

// 设置请求头 (可选)
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setRequestProperty("Accept-Charset", "UTF-8");

int responseCode = connection.getResponseCode();
System.out.println("GET Response Code :: " + responseCode);

if (responseCode == HttpURLConnection.HTTP_OK) { // success
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

// 打印结果
System.out.println("GET Response Body :: " + response.toString());
} else {
System.out.println("GET request not worked");
}
connection.disconnect();
}

// 发送POST请求
private static void sendPostRequest(String urlString, String postData) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// 设置请求方法为POST
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; utf-8");
connection.setRequestProperty("Accept", "application/json");
connection.setDoOutput(true); // 允许写入请求体

// 写入请求体
try(OutputStream os = connection.getOutputStream()) {
byte[] input = postData.getBytes("utf-8");
os.write(input, 0, input.length);
}

int responseCode = connection.getResponseCode();
System.out.println("POST Response Code :: " + responseCode);

if (responseCode == HttpURLConnection.HTTP_CREATED || responseCode == HttpURLConnection.HTTP_OK) { // success
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

System.out.println("POST Response Body :: " + response.toString());
} else {
System.out.println("POST request not worked");
}
connection.disconnect();
}
}

(三)注意事项

  • 需要手动管理连接的打开和关闭。
  • 错误处理和超时设置相对繁琐。
  • 对于HTTPS请求,可能需要额外处理SSL证书。
  • 接口类型适应性
    • RESTful API:对于简单的RESTful API调用(如GET、POST请求,少量参数和头部),HttpURLConnection可以胜任,但所有请求构建、响应解析(如JSON或XML)都需要手动完成,较为繁琐。可以参考[【接口】RESTful API设计指南](../【接口】RESTful API设计指南.md)了解更多RESTful API的细节。
    • SOAP API:理论上可以手动构造SOAP信封并通过POST请求发送,但极其不便,需要手动处理XML的序列化和反序列化。通常对接SOAP服务会使用JAX-WS等专用框架。可以参考[【接口】SOAP API详解](../【接口】SOAP API详解.md)了解更多SOAP API的细节。
    • GraphQL API:由于GraphQL通常通过HTTP POST请求发送JSON格式的查询,HttpURLConnection可以用于发送这些请求。但同样,查询构建和响应处理需要手动进行,缺乏专用GraphQL客户端的便利性。可以参考[【接口】GraphQL API入门](../【接口】GraphQL API入门.md)了解更多GraphQL API的细节。
  • 不建议在需要高性能和复杂交互的场景下作为首选,特别是对于需要复杂请求配置、自动重试、连接池管理的现代应用。 4

二、Apache HttpClient

Apache HttpClient是一个非常成熟和广泛使用的HTTP客户端库,提供了比HttpURLConnection更丰富的功能和更好的灵活性。

(一)特点

  1. 功能强大:支持连接池管理、连接复用、请求/响应拦截器、自定义重试机制、代理配置、Cookie管理、认证机制等。 1
  2. 高度可配置:提供了大量的配置选项,可以精细控制HTTP请求的各个方面。
  3. 成熟稳定:经过长时间的发展和广泛应用,稳定性和可靠性较高。
  4. 社区活跃:拥有庞大的用户社区和丰富的文档资源。 4

(二)使用示例

首先,需要在项目中添加Apache HttpClient的依赖(例如Maven):

1
2
3
4
5
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version> <!-- 请使用最新稳定版本 -->
</dependency>

然后,以下是一个基本的使用示例:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class ApacheHttpClientExample {

public static void main(String[] args) {
// 创建默认的HttpClient实例
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 发送GET请求
sendGetRequest(httpClient, "https://api.example.com/data");

// 发送POST请求
String postData = "{\"name\":\"Jane Doe\", \"age\":25}";
sendPostRequest(httpClient, "https://api.example.com/users", postData);

} catch (IOException e) {
e.printStackTrace();
}
}

// 使用Apache HttpClient发送GET请求
private static void sendGetRequest(CloseableHttpClient httpClient, String url) throws IOException {
HttpGet httpGet = new HttpGet(url);
// httpGet.setHeader("User-Agent", "Apache HttpClient"); // 设置请求头

System.out.println("Executing GET request: " + httpGet.getRequestLine());
HttpResponse response = httpClient.execute(httpGet);

System.out.println("GET Response Status: " + response.getStatusLine().getStatusCode());
HttpEntity entity = response.getEntity();
if (entity != null) {
String responseBody = EntityUtils.toString(entity);
System.out.println("GET Response Body: " + responseBody);
}
EntityUtils.consume(entity); // 确保实体内容被完全消耗,或者关闭InputStream
}

// 使用Apache HttpClient发送POST请求
private static void sendPostRequest(CloseableHttpClient httpClient, String url, String jsonData) throws IOException {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Accept", "application/json");

StringEntity stringEntity = new StringEntity(jsonData);
httpPost.setEntity(stringEntity);

System.out.println("Executing POST request: " + httpPost.getRequestLine());
HttpResponse response = httpClient.execute(httpPost);

System.out.println("POST Response Status: " + response.getStatusLine().getStatusCode());
HttpEntity entity = response.getEntity();
if (entity != null) {
String responseBody = EntityUtils.toString(entity);
System.out.println("POST Response Body: " + responseBody);
}
EntityUtils.consume(entity);
}
}

(三)注意事项

  • 需要正确管理CloseableHttpClient实例的生命周期,通常使用try-with-resources语句确保其关闭。
  • 对于获取的HttpEntity,需要确保其内容被完全消费(例如通过EntityUtils.consume(entity)或读取其InputStream直到末尾),以便连接可以安全地返回到连接池中复用。
  • HttpClient的配置较为复杂,但同时也提供了强大的定制能力。 4
  • 接口类型适应性
    • RESTful API:Apache HttpClient非常适合对接RESTful API。它提供了灵活的请求构建(如设置方法、头部、请求体)、参数编码以及对JSON/XML等常见数据格式的良好支持(通常结合Jackson或JAXB等库进行序列化/反序列化)。其连接池和重试机制对于构建健壮的REST客户端非常有帮助。可以参考[【接口】RESTful API设计指南](../【接口】RESTful API设计指南.md)了解更多RESTful API的细节。
    • SOAP API:虽然HttpClient本身不直接处理SOAP协议的复杂性(如WSDL解析、SOAP信封构造),但它可以作为底层HTTP传输层。许多Java SOAP客户端库(如Apache CXF, JAX-WS RI)允许配置使用HttpClient作为其HTTP transport,从而利用HttpClient的连接管理、超时设置等高级特性。直接使用HttpClient对接SOAP服务需要手动构造XML请求体并解析响应,较为繁琐。可以参考[【接口】SOAP API详解](../【接口】SOAP API详解.md)了解更多SOAP API的细节。
    • GraphQL API:由于GraphQL API通常通过HTTP POST请求(包含JSON格式的查询)进行交互,HttpClient可以很好地支持。开发者需要手动构建JSON查询字符串作为请求体,并解析返回的JSON响应。HttpClient的灵活性使其能够轻松设置必要的头部(如Content-Type: application/json)。可以参考[【接口】GraphQL API入门](../【接口】GraphQL API入门.md)了解更多GraphQL API的细节。

三、OkHttp

OkHttp是由Square公司开发的一款高效的HTTP客户端,专注于性能和易用性。它在Android开发中非常流行,并且在Java服务端也有广泛应用。

(一)特点

  1. 高效性能:支持HTTP/2和SPDY,连接池和Gzip压缩,减少延迟和带宽消耗。 2
  2. 连接池与连接复用:自动管理连接池,有效复用连接。
  3. 请求/响应拦截器:强大的拦截器机制,可以轻松实现日志记录、请求重写、缓存等功能。
  4. 自动重试与故障恢复:当网络出现问题时,OkHttp会自动尝试连接到备用IP地址(如果可用)。 2
  5. 支持同步和异步调用:提供了简洁的API进行同步和异步请求。
  6. 缓存支持:内置响应缓存机制,可以避免不必要的网络请求。 2

(二)使用示例

首先,需要在项目中添加OkHttp的依赖(例如Maven):

1
2
3
4
5
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version> <!-- 请使用最新稳定版本 -->
</dependency>

然后,以下是一个基本的使用示例:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import okhttp3.*;
import java.io.IOException;

public class OkHttpExample {

// 创建OkHttpClient实例,建议全局共享一个实例
private static final OkHttpClient client = new OkHttpClient();

public static void main(String[] args) {
try {
// 发送GET请求
sendGetRequest("https://api.example.com/data");

// 发送POST请求
String jsonData = "{\"name\":\"OkHttp User\", \"job\":\"Tester\"}";
sendPostRequest("https://api.example.com/users", jsonData);

} catch (IOException e) {
e.printStackTrace();
}
}

// 使用OkHttp发送GET请求
private static void sendGetRequest(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
// .addHeader("User-Agent", "OkHttp Example") // 添加请求头
.build();

try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println("GET Response Code: " + response.code());
if (response.body() != null) {
System.out.println("GET Response Body: " + response.body().string());
}
}
}

// 使用OkHttp发送POST请求
private static void sendPostRequest(String url, String json) throws IOException {
RequestBody body = RequestBody.create(
json, MediaType.get("application/json; charset=utf-f")
);

Request request = new Request.Builder()
.url(url)
.post(body)
.build();

try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println("POST Response Code: " + response.code());
if (response.body() != null) {
System.out.println("POST Response Body: " + response.body().string());
}
}
}
}

(三)注意事项

  • OkHttpClient实例是线程安全的,并且包含自己的连接池和线程池,建议在应用程序中共享同一个OkHttpClient实例以获得最佳性能。
  • 使用try-with-resources语句来确保Response对象被正确关闭,即使在发生异常时也是如此。这对于释放底层资源(如连接)非常重要。
  • response.body().string()只能被调用一次。如果需要多次读取响应体,应先将其读取到内存中或使用peekBody()方法。
  • OkHttp提供了丰富的配置选项,如超时设置、代理、拦截器等,可以根据需要进行定制。 4
  • 接口类型适应性
    • RESTful API:OkHttp 是对接 RESTful API 的优秀选择。其简洁的API设计、高效的性能(HTTP/2支持、连接池)、强大的拦截器机制以及对请求/响应的精细控制,使其非常适合构建现代REST客户端。它能轻松处理JSON、XML等数据格式(通常结合Gson、Jackson等库)。可以参考[【接口】RESTful API设计指南](../【接口】RESTful API设计指南.md)了解更多RESTful API的细节。
    • SOAP API:与Apache HttpClient类似,OkHttp本身不直接处理SOAP协议的语义。但它可以作为高效的HTTP传输层。一些现代SOAP客户端库可能允许配置OkHttp作为底层HTTP客户端。若直接使用OkHttp对接SOAP,需要手动构造SOAP XML请求体并解析响应,这通常比较复杂。可以参考[【接口】SOAP API详解](../【接口】SOAP API详解.md)了解更多SOAP API的细节。
    • GraphQL API:OkHttp 非常适合用于调用GraphQL API。由于GraphQL查询通常作为JSON对象通过HTTP POST请求发送,OkHttp可以方便地构建此类请求(包括设置Content-Type: application/json头部和JSON请求体),并处理返回的JSON响应。其拦截器机制也可用于添加认证头部或日志记录。可以参考[【接口】GraphQL API入门](../【接口】GraphQL API入门.md)了解更多GraphQL API的细节。

四、Spring RestTemplate

RestTemplate是Spring框架提供的一个同步的、阻塞式的HTTP客户端,用于简化与RESTful服务的交互。它是Spring生态中非常常用的HTTP客户端之一。

(一)特点

  1. Spring集成:与Spring框架无缝集成,易于配置和使用,尤其在Spring Boot项目中。
  2. 简化API调用:提供了多种便捷方法(如getForObjectpostForEntityexchange等)来执行HTTP请求并处理响应,大大简化了客户端代码。 1
  3. 自动转换:支持自动将请求体和响应体在Java对象和JSON/XML等格式之间进行转换(通过HttpMessageConverter)。
  4. 错误处理:提供了ResponseErrorHandler来自定义错误处理逻辑。
  5. 底层可替换:虽然默认使用HttpURLConnection,但可以配置为使用其他HTTP客户端库(如Apache HttpClient, OkHttp)作为底层实现。 4

(二)使用示例

首先,如果是在Spring Boot项目中使用,通常spring-boot-starter-web会包含RestTemplate。如果是非Spring Boot项目,需要添加spring-web依赖。

1
2
3
4
5
6
7
8
9
10
11
12
<!-- For Spring Boot projects, spring-boot-starter-web is usually sufficient -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Or for non-Spring Boot Spring projects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version> <!-- Use appropriate Spring version -->
</dependency>

然后,以下是一个基本的使用示例:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;

public class RestTemplateExample {

// 通常通过依赖注入获取RestTemplate实例
private final RestTemplate restTemplate;

public RestTemplateExample(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}

// 示例:配置RestTemplate Bean (在Spring配置类中)
// @Bean
// public RestTemplate restTemplate() {
// return new RestTemplate();
// }

public static void main(String[] args) {
// 在非Spring管理的环境下,可以直接创建实例
RestTemplate staticRestTemplate = new RestTemplate();
RestTemplateExample example = new RestTemplateExample(staticRestTemplate);

// 发送GET请求
example.sendGetRequest("https://api.example.com/data");

// 发送POST请求
Map<String, Object> postData = new HashMap<>();
postData.put("name", "RestTemplate User");
postData.put("email", "user@example.com");
example.sendPostRequest("https://api.example.com/users", postData);
}

public void sendGetRequest(String url) {
try {
// getForObject: 直接获取响应体并转换为指定类型
String responseBody = restTemplate.getForObject(url, String.class);
System.out.println("GET Response Body (getForObject): " + responseBody);

// getForEntity: 获取包含完整响应信息(状态码、头、体)的ResponseEntity
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
System.out.println("GET Response Status (getForEntity): " + responseEntity.getStatusCode());
System.out.println("GET Response Headers (getForEntity): " + responseEntity.getHeaders());
System.out.println("GET Response Body (getForEntity): " + responseEntity.getBody());

} catch (Exception e) {
System.err.println("Error during GET request: " + e.getMessage());
}
}

public void sendPostRequest(String url, Object requestBody) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);

// postForObject: 发送POST请求,直接获取响应体
// String responseBody = restTemplate.postForObject(url, requestEntity, String.class);
// System.out.println("POST Response Body (postForObject): " + responseBody);

// postForEntity: 发送POST请求,获取ResponseEntity
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
System.out.println("POST Response Status (postForEntity): " + responseEntity.getStatusCode());
System.out.println("POST Response Body (postForEntity): " + responseEntity.getBody());

} catch (Exception e) {
System.err.println("Error during POST request: " + e.getMessage());
}
}
}

(三)注意事项

  • RestTemplate是同步阻塞的,对于需要高并发、非阻塞I/O的场景,Spring推荐使用WebClient
  • 错误处理:默认情况下,RestTemplate在遇到HTTP 4xx或5xx响应时会抛出HttpClientErrorExceptionHttpServerErrorException。可以通过实现ResponseErrorHandler接口来自定义错误处理行为。
  • 配置:可以通过RestTemplateBuilder或直接配置ClientHttpRequestFactory来定制RestTemplate的行为,例如设置超时、使用连接池(如配置Apache HttpClient或OkHttp作为底层实现)。
  • 从Spring Framework 5开始,RestTemplate进入维护模式,虽然仍然可用,但Spring官方更推荐在新的响应式应用中使用WebClient。对于传统的Servlet栈应用,RestTemplate依然是一个不错的选择。 1
  • 接口类型适应性
    • RESTful APIRestTemplate是为RESTful服务交互而设计的,因此非常适合对接RESTful API。它提供了便捷的方法(如getForObjectpostForEntityexchange)并能自动处理HTTP头部、请求/响应体的序列化和反序列化(通常使用内置的HttpMessageConverter,如Jackson处理JSON)。可以参考[【接口】RESTful API设计指南](../【接口】RESTful API设计指南.md)了解更多RESTful API的细节。
    • SOAP API:虽然RestTemplate主要面向REST,但理论上也可以用于发送原始的XML(SOAP)请求。然而,这需要手动构造SOAP信封XML作为请求体,并手动解析SOAP响应。Spring生态中有更专门的SOAP客户端支持,如WebServiceTemplate(来自Spring Web Services项目),它更适合处理SOAP的复杂性。直接使用RestTemplate对接SOAP服务会比较繁琐。可以参考[【接口】SOAP API详解](../【接口】SOAP API详解.md)了解更多SOAP API的细节。
    • GraphQL API:由于GraphQL API通常通过HTTP POST请求(携带JSON格式的查询)进行通信,RestTemplate可以很好地用于此目的。开发者需要将GraphQL查询构造成JSON字符串,并使用RestTemplate的POST方法发送。响应也是JSON格式,可以由RestTemplate自动转换为Java对象。可以参考[【接口】GraphQL API入门](../【接口】GraphQL API入门.md)了解更多GraphQL API的细节。

五、Spring WebClient

WebClient是Spring Framework 5引入的非阻塞、响应式的HTTP客户端,构建于Project Reactor之上。它是Spring WebFlux栈的一部分,但也可以在传统的Servlet栈应用中使用。WebClient被认为是RestTemplate的现代替代品,尤其适用于需要高并发和异步处理的场景。

(一)特点

  1. 非阻塞和响应式:基于响应式流(Reactive Streams)规范,能够以非阻塞方式处理HTTP请求和响应,提高系统吞吐量和伸缩性。
  2. 函数式API:提供了流畅的函数式API来构建请求和处理响应,代码更具表现力。
  3. 支持HTTP/2:底层可以利用支持HTTP/2的客户端(如Netty)。
  4. 流式处理:非常适合处理流式数据,例如Server-Sent Events (SSE) 或大型文件下载/上传。
  5. 与Spring WebFlux集成:作为Spring WebFlux的核心组件,与响应式编程模型完美契合。

(二)使用示例

首先,需要在项目中添加spring-boot-starter-webflux依赖(如果使用Spring Boot):

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

然后,以下是一个基本的使用示例:

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Map;

@Component
public class WebClientExample {

private final WebClient webClient;

// 推荐通过构造函数注入预配置的WebClient.Builder
public WebClientExample(WebClient.Builder webClientBuilder) {
// 可以基于Builder进行通用配置,如baseUrl, defaultHeaders等
this.webClient = webClientBuilder.baseUrl("https://api.example.com").build();
}

// 或者直接创建一个实例 (不推荐在Spring管理环境)
// private final WebClient webClient = WebClient.create("https://api.example.com");

public static void main(String[] args) {
// 在非Spring管理的环境下,可以这样创建和使用
WebClient staticWebClient = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();

// 示例调用 (注意:在main方法中直接调用block()仅为演示,实际响应式编程中应避免阻塞)
// GET请求
Mono<String> getResponseBodyMono = staticWebClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class);

getResponseBodyMono.subscribe(
responseBody -> System.out.println("GET Response Body: " + responseBody),
error -> System.err.println("Error during GET request: " + error.getMessage())
);

// POST请求
Map<String, String> postData = Map.of("name", "WebClient User", "title", "Developer");
Mono<String> postResponseBodyMono = staticWebClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(postData) // 或者 .body(Mono.just(postData), Map.class)
.retrieve()
.bodyToMono(String.class);

postResponseBodyMono.subscribe(
responseBody -> System.out.println("POST Response Body: " + responseBody),
error -> System.err.println("Error during POST request: " + error.getMessage())
);

// 为了让异步操作在main方法结束前执行完毕,这里简单sleep一下
// 在实际应用中,通常是在一个响应式流程中处理,或者在测试中使用StepVerifier
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// 封装的GET请求方法
public Mono<String> fetchData(String path) {
return this.webClient.get()
.uri(path)
.accept(MediaType.APPLICATION_JSON)
.retrieve() // 获取响应体
.bodyToMono(String.class) // 将响应体转换为Mono<String>
.doOnError(error -> System.err.println("Request failed: " + error.getMessage()));
}

// 封装的POST请求方法
public <T> Mono<String> postData(String path, T data) {
return this.webClient.post()
.uri(path)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.bodyValue(data)
.retrieve()
.bodyToMono(String.class)
.doOnError(error -> System.err.println("POST Request failed: " + error.getMessage()));
}
}

(三)注意事项

  • WebClient是非阻塞的,基于响应式编程模型(Project Reactor),适用于高并发、I/O密集型应用。
  • 学习曲线相对RestTemplate更陡峭,需要理解响应式流(Publisher、Mono、Flux)的概念。
  • 错误处理采用响应式操作符(如onErrorResumeretry等),与传统try-catch方式不同。
  • 虽然主要用于WebFlux应用,但也可以在传统的Servlet栈应用中使用(通过添加Reactor Netty等依赖并适当配置)。
  • 对于简单的同步阻塞调用,RestTemplate可能仍然更直接。 3
  • 接口类型适应性
    • RESTful APIWebClient是Spring 5+ 中对接RESTful API的首选方式,尤其是在响应式应用中。它提供了流式的API来构建请求、处理响应,并能与Project Reactor无缝集成,实现完全非阻塞的交互。它同样支持通过HttpMessageReaderHttpMessageWriter进行自动的请求/响应体转换。可以参考[【接口】RESTful API设计指南](../【接口】RESTful API设计指南.md)了解更多RESTful API的细节。
    • SOAP API:与RestTemplate类似,WebClient主要设计用于RESTful交互。虽然理论上可以发送原始XML(SOAP)请求,但这需要手动构造SOAP信封并解析响应,且其响应式特性可能难以充分利用SOAP的同步阻塞本质。对于SOAP服务,通常还是推荐使用Spring Web Services中的WebServiceTemplate或专门的SOAP客户端库。可以参考[【接口】SOAP API详解](../【接口】SOAP API详解.md)了解更多SOAP API的细节。
    • GraphQL APIWebClient非常适合用于调用基于HTTP的GraphQL API。开发者可以构建包含JSON格式GraphQL查询的POST请求,并通过WebClient以非阻塞方式发送。响应的JSON数据也可以通过响应式流进行处理和转换。Spring Boot 2.7+ 还引入了对Spring for GraphQL项目的支持,提供了更高级别的GraphQL客户端抽象。可以参考[【接口】GraphQL API入门](../【接口】GraphQL API入门.md)了解更多GraphQL API的细节。

六、总结与选择建议

选择哪种HTTP客户端取决于项目的具体需求、技术栈以及团队的熟悉程度:

  1. HttpURLConnection

    • 优点:JDK自带,无需额外依赖,简单请求易于上手。
    • 缺点:功能基础,API繁琐,无连接池,性能较低,不适合复杂或高并发场景。
    • 建议:仅适用于非常简单的、对性能要求不高的临时性HTTP调用,或者在严格限制外部依赖的环境中。
  2. Apache HttpClient

    • 优点:功能强大且全面,高度可配置,成熟稳定,社区支持广泛。
    • 缺点:API相对OkHttp和Spring客户端略显复杂,配置项较多。
    • 建议:适用于需要精细控制HTTP请求、高级特性(如复杂认证、代理、连接管理)的传统Java应用。如果项目中已经在使用,并且团队熟悉,可以继续使用。
  3. OkHttp

    • 优点:性能高效,API简洁易用,支持HTTP/2,强大的拦截器和缓存机制,Android开发首选。
    • 缺点:相比Spring的客户端,与Spring生态的集成需要额外配置(尽管Spring也支持将其作为RestTemplateWebClient的底层实现)。
    • 建议:追求高性能、简洁API的场景,尤其是在非Spring生态或对HTTP/2有明确需求时。也是一个非常好的通用HTTP客户端选择。 4
  4. Spring RestTemplate

    • 优点:与Spring框架无缝集成,API相对HttpURLConnectionHttpClient更简单,支持自动对象转换。
    • 缺点:同步阻塞模型,不适合高并发响应式应用。Spring官方已将其置于维护模式,推荐新项目使用WebClient
    • 建议:对于已有的、基于Spring MVC的传统Servlet应用,如果不需要响应式特性,RestTemplate仍然是一个方便的选择。但新项目应优先考虑WebClient
  5. Spring WebClient

    • 优点:非阻塞、响应式,性能高,适用于高并发场景,函数式API现代且易于组合,与Spring WebFlux完美集成。
    • 缺点:需要理解响应式编程范式,对于习惯了同步编程的开发者有一定学习曲线。
    • 建议:Spring生态下的首选HTTP客户端,特别是对于新建的、需要高并发处理能力或采用响应式编程模型的应用。

在实际开发中,开发者应根据项目的技术栈(是否使用Spring)、性能要求(同步/异步、并发量)、团队对特定库的熟悉程度以及所需特性(如HTTP/2、流式处理等)来综合考量,选择最合适的HTTP客户端工具。

七、参考资料

(一)外部文章与官方文档

  • HttpClient、OKhttp、RestTemplate接口调用对比,选择一… - CSDN博客 1
  • Java模拟http(s)请求-HttpURLConnection、HttpClient、OkHt… - CSDN博客 2
  • RestTemplate vs WebClient vs HttpClient: A Comparison | by Sainath … - Medium 3
  • Which Java HTTP client should I use in 2024? - WireMock 4
  • Spring Framework Documentation: RestTemplate
  • Spring Framework Documentation: WebClient
  • OkHttp Official Website: OkHttp
  • Apache HttpClient Official Website: Apache HttpClient

(二)本站相关笔记

  • 【接口】接口类型总览与对比
  • [【接口】RESTful API设计指南](../【接口】RESTful API设计指南.md)
  • [【接口】SOAP API详解](../【接口】SOAP API详解.md)
  • [【接口】GraphQL API入门](../【接口】GraphQL API入门.md)