文章目录
1. HTTP 和 HTTPS 基础知识
1.1 什么是 HTTP/HTTPS?
HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是 Web 的基础,用于在 Web 浏览器和服务器之间传输数据,如HTML文件、图像、查询结果等。
HTTPS(HTTP Secure,安全超文本传输协议)是 HTTP 的安全版本,通过 SSL/TLS 协议进行加密通信,提供了三个关键安全功能:
- 加密:防止数据在传输过程中被第三方窃听
- 数据完整性:防止数据被篡改
- 认证:确保用户正在与预期的网站通信
1.2 HTTP 请求与响应结构
HTTP 请求由以下部分组成:
- 请求行:包含HTTP方法(GET、POST等)、URL和HTTP版本
- 请求头:包含有关请求的附加信息(如Content-Type、User-Agent等)
- 空行:分隔请求头和请求体
- 请求体:包含发送到服务器的数据(如POST请求中的表单数据)
HTTP 响应由以下部分组成:
- 状态行:包含HTTP版本、状态码和状态消息
- 响应头:包含有关响应的附加信息
- 空行:分隔响应头和响应体
- 响应体:包含服务器返回的数据
1.3 常见的 HTTP 方法
- GET:请求指定的资源,应该只被用于获取数据
- POST:向指定资源提交数据,可能导致新资源的创建或已有资源的修改
- PUT:使用请求中的数据替换指定资源的内容
- DELETE:删除指定的资源
- PATCH:对资源应用部分修改
- HEAD:与GET相同,但只返回响应头,不返回响应体
- OPTIONS:获取目标资源支持的通信选项
1.4 常见的 HTTP 状态码
- 1xx(信息性):请求已接收,继续处理
- 2xx(成功):请求已成功接收、理解、接受
- 200 OK:请求成功
- 201 Created:请求已完成,并创建了新资源
- 204 No Content:服务器成功处理了请求,但没有返回任何内容
- 3xx(重定向):需要进一步操作才能完成请求
- 301 Moved Permanently:资源已永久移动到新位置
- 302 Found:资源临时位于不同的URL
- 304 Not Modified:资源未被修改
- 4xx(客户端错误):请求包含错误语法或无法完成
- 400 Bad Request:服务器无法理解请求
- 401 Unauthorized:需要身份验证
- 403 Forbidden:服务器拒绝请求
- 404 Not Found:服务器找不到请求的资源
- 5xx(服务器错误):服务器在处理请求时发生错误
- 500 Internal Server Error:服务器遇到了意外情况
- 502 Bad Gateway:服务器作为网关,从上游服务器收到无效响应
- 503 Service Unavailable:服务器暂时不可用
2. Java 原生 HTTP 客户端
2.1 使用 URLConnection 和 HttpURLConnection
URLConnection
和 HttpURLConnection
是 Java 标准库提供的最基本的 HTTP 客户端 API,自 JDK 1.1 就存在。虽然功能相对简单,但不需要任何外部依赖。
2.1.1 基本 GET 请求
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpURLConnectionExample {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://api.example.com/data");
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法(默认为GET)
connection.setRequestMethod("GET");
// 设置连接和读取超时(毫秒)
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 获取响应状态码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 读取响应内容
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印响应内容
System.out.println("Response: " + response.toString());
// 断开连接
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.1.2 基本 POST 请求
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class HttpURLConnectionPostExample {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://api.example.com/submit");
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法
connection.setRequestMethod("POST");
// 启用输入/输出流
connection.setDoOutput(true);
connection.setDoInput(true);
// 设置请求头
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
// 构建请求体
String jsonInputString = "{\"name\": \"John Doe\", \"email\": \"john@example.com\"}";
// 发送请求体
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
outputStream.write(input, 0, input.length);
}
// 获取响应状态码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 读取响应内容
BufferedReader in;
if (responseCode >= 200 && responseCode < 300) {
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
} else {
in = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
}
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印响应内容
System.out.println("Response: " + response.toString());
// 断开连接
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.1.3 处理 HTTPS 请求
处理 HTTPS 请求需要配置 HttpsURLConnection
,特别是在需要自定义信任管理器或者客户端证书时:
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.X509Certificate;
public class HttpsURLConnectionExample {
public static void main(String[] args) {
try {
// 创建信任所有证书的信任管理器(仅用于测试/开发环境!)
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { }
}
};
// 创建SSL上下文,并使用我们的信任管理器
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// 创建URL对象
URL url = new URL("https://api.example.com/secure-data");
// 打开连接
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
// 设置SSL socket工厂
connection.setSSLSocketFactory(sslContext.getSocketFactory());
// 设置主机名验证器(跳过主机名验证,仅用于测试)
connection.setHostnameVerifier((hostname, session) -> true);
// 设置请求方法
connection.setRequestMethod("GET");
// 获取响应状态码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 读取响应内容
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印响应内容
System.out.println("Response: " + response.toString());
// 断开连接
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:上述示例中使用了接受所有证书的信任管理器,这仅应在开发/测试环境中使用,生产环境应使用适当的证书验证。
2.1.4 设置代理
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
public class HttpURLConnectionProxyExample {
public static void main(String[] args) {
try {
// 创建代理对象
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080));
// 创建URL对象
URL url = new URL("https://api.example.com/data");
// 使用代理打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
// 设置请求方法
connection.setRequestMethod("GET");
// 如果代理需要身份验证
String authString = "username:password";
byte[] authEncBytes = java.util.Base64.getEncoder().encode(authString.getBytes());
String authStringEnc = new String(authEncBytes);
connection.setRequestProperty("Proxy-Authorization", "Basic " + authStringEnc);
// 获取响应状态码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 读取响应内容
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印响应内容
System.out.println("Response: " + response.toString());
// 断开连接
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.1.5 上传文件
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class HttpURLConnectionFileUploadExample {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://api.example.com/upload");
// 连接参数
String boundary = "===" + System.currentTimeMillis() + "===";
// 打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法
connection.setRequestMethod("POST");
// 启用输入/输出流
connection.setDoOutput(true);
connection.setDoInput(true);
// 设置请求头
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
// 创建请求体
try (OutputStream outputStream = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), true)) {
// 添加表单字段
writer.append("--").append(boundary).append("\r\n");
writer.append("Content-Disposition: form-data; name=\"description\"\r\n\r\n");
writer.append("文件描述").append("\r\n");
// 添加文件
writer.append("--").append(boundary).append("\r\n");
writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"example.txt\"\r\n");
writer.append("Content-Type: text/plain\r\n\r\n");
writer.flush();
// 从文件读取并写入请求
try (FileInputStream fileInputStream = new FileInputStream("example.txt")) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
writer.append("\r\n");
// 结束请求
writer.append("--").append(boundary).append("--").append("\r\n");
}
// 获取响应状态码
int responseCode = connection.getResponseCode();
System.out.println("Response Code: " + responseCode);
// 读取响应内容
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// 打印响应内容
System.out.println("Response: " + response.toString());
// 断开连接
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 使用 Java 11+ HttpClient
从 Java 11 开始,Java 标准库引入了现代化的 HttpClient
API,提供了比 HttpURLConnection
更丰富的功能,包括异步请求、WebSocket 支持等。
2.2.1 基本 GET 请求
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Java11HttpClientExample {
public static void main(String[] args) {
try {
// 创建 HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 设置HTTP协议版本
.connectTimeout(Duration.ofSeconds(5)) // 设置连接超时
.build();
// 创建 HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.timeout(Duration.ofSeconds(5)) // 设置请求超时
.header("Content-Type", "application/json")
.GET() // 设置请求方法为GET
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 获取响应状态码
int statusCode = response.statusCode();
System.out.println("Status Code: " + statusCode);
// 获取响应头
response.headers().map().forEach((key, values) -> {
System.out.println(key + ": " + values);
});
// 获取响应体
String responseBody = response.body();
System.out.println("Response Body: " + responseBody);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
2.2.2 基本 POST 请求
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Java11HttpClientPostExample {
public static void main(String[] args) {
try {
// 创建 HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(5))
.build();
// 准备JSON请求体
String jsonBody = "{\"name\": \"John Doe\", \"email\": \"john@example.com\"}";
// 创建 HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/submit"))
.timeout(Duration.ofSeconds(5))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody)) // 设置请求方法为POST并提供请求体
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 获取响应状态码
int statusCode = response.statusCode();
System.out.println("Status Code: " + statusCode);
// 获取响应体
String responseBody = response.body();
System.out.println("Response Body: " + responseBody);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
2.2.3 异步请求
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public class Java11HttpClientAsyncExample {
public static void main(String[] args) {
// 创建 HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(5))
.build();
// 创建 HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.timeout(Duration.ofSeconds(5))
.GET()
.build();
// 异步发送请求
CompletableFuture<HttpResponse<String>> futureResponse =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
// 添加响应处理逻辑
futureResponse
.thenApply(response -> {
System.out.println("Status Code: " + response.statusCode());
return response;
})
.thenApply(HttpResponse::body)
.thenAccept(responseBody -> System.out.println("Response Body: " + responseBody))
.exceptionally(e -> {
System.err.println("Error: " + e.getMessage());
return null;
});
// 等待异步操作完成
futureResponse.join();
System.out.println("请求已完成");
}
}
2.2.4 处理 HTTPS 和 SSL
import javax.net.ssl.*;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.time.Duration;
public class Java11HttpClientSslExample {
public static void main(String[] args) {
try {
// 方法1:使用默认的SSL配置
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
HttpClient defaultClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.sslContext(sslContext)
.build();
// 方法2:配置自定义SSL上下文(允许所有证书,仅用于测试)
SSLContext sslContext2 = SSLContext.getInstance("TLS");
sslContext2.init(null, null, null);
HttpClient customSslClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.sslContext(sslContext2)
.build();
// 方法3:使用客户端证书进行双向SSL认证
KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
clientKeyStore.load(
Java11HttpClientSslExample.class.getResourceAsStream("/client.p12"),
"password".toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, "password".toCharArray());
SSLContext clientAuthSslContext = SSLContext.getInstance("TLS");
clientAuthSslContext.init(
keyManagerFactory.getKeyManagers(),
null, // 使用默认的信任管理器
null); // 使用默认的随机数生成器
HttpClient clientAuthClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.sslContext(clientAuthSslContext)
.build();
// 创建请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/secure-data"))
.timeout(Duration.ofSeconds(5))
.GET()
.build();
// 使用适当的客户端发送请求
HttpResponse<String> response = customSslClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status Code: " + response.statusCode());
System.out.println("Response Body: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2.5 上传文件
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;
import java.time.Duration;
public class Java11HttpClientFileUploadExample {
public static void main(String[] args) {
try {
// 创建 HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(30)) // 上传文件可能需要更长的超时时间
.build();
// 创建多部分请求
var multipartData = MultipartBodyPublisher.newBuilder()
.addPart("description", "文件描述")
.addFilePart("file", Path.of("example.txt"))
.build();
// 创建 HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/upload"))
.timeout(Duration.ofSeconds(60))
.header("Content-Type", "multipart/form-data; boundary=" + multipartData.getBoundary())
.POST(multipartData)
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 获取响应状态码
int statusCode = response.statusCode();
System.out.println("Status Code: " + statusCode);
// 获取响应体
String responseBody = response.body();
System.out.println("Response Body: " + responseBody);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
// 自定义多部分请求体发布器
static class MultipartBodyPublisher {
private final String boundary;
private final HttpRequest.BodyPublisher bodyPublisher;
private MultipartBodyPublisher(String boundary, HttpRequest.BodyPublisher bodyPublisher) {
this.boundary = boundary;
this.bodyPublisher = bodyPublisher;
}
public String getBoundary() {
return boundary;
}
public HttpRequest.BodyPublisher bodyPublisher() {
return bodyPublisher;
}
public static Builder newBuilder() {
return new Builder();
}
static class Builder {
private final String boundary = "===" + System.currentTimeMillis() + "===";
private final StringBuilder content = new StringBuilder();
public Builder addPart(String name, String value) {
content.append("--").append(boundary).append("\r\n");
content.append("Content-Disposition: form-data; name=\"").append(name).append("\"\r\n\r\n");
content.append(value).append("\r\n");
return this;
}
public Builder addFilePart(String name, Path filePath) throws IOException {
content.append("--").append(boundary).append("\r\n");
content.append("Content-Disposition: form-data; name=\"").append(name)
.append("\"; filename=\"").append(filePath.getFileName()).append("\"\r\n");
content.append("Content-Type: application/octet-stream\r\n\r\n");
// 为简化示例,这里将文件内容也作为字符串处理
// 实际应用中应使用byte数组或流处理二进制内容
content.append(java.nio.file.Files.readString(filePath)).append("\r\n");
return this;
}
public HttpRequest.BodyPublisher build() {
content.append("--").append(boundary).append("--\r\n");
return HttpRequest.BodyPublishers.ofString(content.toString());
}
}
}
3. Apache HttpClient
Apache HttpClient 是 Apache HttpComponents 项目的一部分,提供了功能丰富、标准兼容的 HTTP 客户端库。它支持 HTTP/1.1 和 HTTP/2,并具有许多高级功能,如连接池、认证支持、重试机制等。
3.1 添加依赖
Maven:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>
Gradle:
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
3.2 基本 GET 请求
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
public class ApacheHttpClientGetExample {
public static void main(String[] args) {
// 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 创建 HttpGet 请求
HttpGet httpGet = new HttpGet("https://api.example.com/data");
// 设置请求头
httpGet.setHeader("User-Agent", "Mozilla/5.0");
httpGet.setHeader("Accept", "application/json");
// 发送 GET 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.3 基本 POST 请求
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import java.io.IOException;
public class ApacheHttpClientPostExample {
public static void main(String[] args) {
// 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 创建 HttpPost 请求
HttpPost httpPost = new HttpPost("https://api.example.com/submit");
// 设置请求头
httpPost.setHeader("User-Agent", "Mozilla/5.0");
httpPost.setHeader("Accept", "application/json");
// 设置请求体
String jsonBody = "{\"name\": \"John Doe\", \"email\": \"john@example.com\"}";
StringEntity entity = new StringEntity(jsonBody, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 发送 POST 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.4 配置连接池
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.util.Timeout;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class ApacheHttpClientConnectionPoolExample {
public static void main(String[] args) {
// 创建连接管理器
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
// 设置最大连接数
connectionManager.setMaxTotal(100);
// 设置每个路由的最大连接数
connectionManager.setDefaultMaxPerRoute(20);
// 配置请求参数
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.of(5, TimeUnit.SECONDS))
.setResponseTimeout(Timeout.of(5, TimeUnit.SECONDS))
.setConnectionRequestTimeout(Timeout.of(5, TimeUnit.SECONDS))
.build();
// 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.build()) {
// 创建 HttpGet 请求
HttpGet httpGet = new HttpGet("https://api.example.com/data");
// 发送 GET 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.5 处理 Cookie
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
import java.util.Date;
import java.util.List;
public class ApacheHttpClientCookieExample {
public static void main(String[] args) {
// 创建 Cookie 存储
BasicCookieStore cookieStore = new BasicCookieStore();
// 添加自定义 Cookie
BasicClientCookie cookie = new BasicClientCookie("sessionId", "abc123");
cookie.setDomain("example.com");
cookie.setPath("/");
// 设置过期时间(1小时后)
cookie.setExpiryDate(new Date(System.currentTimeMillis() + 3600 * 1000));
cookieStore.addCookie(cookie);
// 创建 HttpClient 实例,使用 Cookie 存储
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build()) {
// 创建 HttpGet 请求
HttpGet httpGet = new HttpGet("https://api.example.com/data");
// 发送 GET 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
// 获取响应中的 Cookie
List<Cookie> cookies = cookieStore.getCookies();
for (Cookie c : cookies) {
System.out.println("Cookie: " + c.getName() + " = " + c.getValue());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.6 表单提交
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ApacheHttpClientFormPostExample {
public static void main(String[] args) {
// 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 创建 HttpPost 请求
HttpPost httpPost = new HttpPost("https://api.example.com/submit-form");
// 创建表单参数列表
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("username", "john_doe"));
params.add(new BasicNameValuePair("password", "secret123"));
params.add(new BasicNameValuePair("remember", "true"));
// 设置表单实体
httpPost.setEntity(new UrlEncodedFormEntity(params));
// 发送 POST 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.7 文件上传
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.File;
import java.io.IOException;
public class ApacheHttpClientFileUploadExample {
public static void main(String[] args) {
// 需要额外添加依赖:httpmime
// <dependency>
// <groupId>org.apache.httpcomponents.client5</groupId>
// <artifactId>httpclient5-fluent</artifactId>
// <version>5.2.1</version>
// </dependency>
// 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 创建 HttpPost 请求
HttpPost httpPost = new HttpPost("https://api.example.com/upload");
// 创建 MultipartEntityBuilder
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
// 添加文本字段
builder.addTextBody("description", "文件描述", ContentType.TEXT_PLAIN);
// 添加文件
File file = new File("example.txt");
builder.addBinaryBody(
"file",
file,
ContentType.APPLICATION_OCTET_STREAM,
file.getName()
);
// 设置请求实体
httpPost.setEntity(builder.build());
// 发送 POST 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.8 配置 HTTPS 和 SSL
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.TrustStrategy;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class ApacheHttpClientSslExample {
public static void main(String[] args) {
try {
// 方法1:信任所有证书(仅用于测试/开发环境!)
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
})
.build();
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory)
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// 创建 HttpClient 实例
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.build()) {
// 创建 HttpGet 请求
HttpGet httpGet = new HttpGet("https://api.example.com/secure-data");
// 发送 GET 请求并获取响应
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
// 获取响应状态码
int statusCode = response.getCode();
System.out.println("Status Code: " + statusCode);
// 获取响应实体
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("Response Body: " + responseBody);
}
}
// 方法2:使用客户端证书进行双向SSL认证
SSLContext clientAuthSslContext = new SSLContextBuilder()
.loadTrustMaterial(new File("truststore.jks"), "truststorePassword".toCharArray())
.loadKeyMaterial(new File("keystore.jks"), "keystorePassword".toCharArray(), "keyPassword".toCharArray())
.build();
SSLConnectionSocketFactory clientAuthSslSocketFactory = new SSLConnectionSocketFactory(clientAuthSslContext);
try (CloseableHttpClient clientAuthHttpClient = HttpClients.custom()
.setSSLSocketFactory(clientAuthSslSocketFactory)
.build()) {
// 使用双向SSL认证的HttpClient发送请求
// ...
}
} catch (IOException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
e.printStackTrace();
}
}
}
4. OkHttp
OkHttp 是一个由 Square 开发的高效 HTTP 客户端,专为 Android 和 Java 应用程序设计。它支持 HTTP/2 和 SPDY,可以共享同一地址的 socket 连接,使用连接池减少请求延迟,并提供透明的 GZIP 压缩和响应缓存。
4.1 添加依赖
Maven:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
Gradle:
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
4.2 基本 GET 请求
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class OkHttpGetExample {
// OkHttpClient 实例应该是单例的,在应用程序生命周期内重用
private final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
OkHttpGetExample example = new OkHttpGetExample();
try {
String response = example.run("https://api.example.com/data");
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
private String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.header("User-Agent", "OkHttp Example")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response);
}
return response.body().string();
}
}
}
4.3 基本 POST 请求
import okhttp3.*;
import java.io.IOException;
public class OkHttpPostExample {
private final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
OkHttpPostExample example = new OkHttpPostExample();
try {
String response = example.post("https://api.example.com/submit");
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
private String post(String url) throws IOException {
// 设置请求体的媒体类型
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String jsonBody = "{\"name\": \"John Doe\", \"email\": \"john@example.com\"}";
RequestBody body = RequestBody.create(jsonBody, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response);
}
return response.body().string();
}
}
}
4.4 异步请求
import okhttp3.*;
import java.io.IOException;
public class OkHttpAsyncExample {
private final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
OkHttpAsyncExample example = new OkHttpAsyncExample();
example.run("https://api.example.com/data");
// 等待异步请求完成
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void run(String url) {
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response);
}
System.out.println(responseBody.string());
}
}
});
System.out.println("请求已发送,等待响应...");
}
}
4.5 表单提交
import okhttp3.*;
import java.io.IOException;
public class OkHttpFormPostExample {
private final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
OkHttpFormPostExample example = new OkHttpFormPostExample();
try {
String response = example.formPost("https://api.example.com/submit-form");
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
private String formPost(String url) throws IOException {
RequestBody formBody = new FormBody.Builder()
.add("username", "john_doe")
.add("password", "secret123")
.add("remember", "true")
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response);
}
return response.body().string();
}
}
}
4.6 文件上传
import okhttp3.*;
import java.io.File;
import java.io.IOException;
public class OkHttpFileUploadExample {
private final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
OkHttpFileUploadExample example = new OkHttpFileUploadExample();
try {
String response = example.uploadFile("https://api.example.com/upload");
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
private String uploadFile(String url) throws IOException {
File file = new File("example.txt");
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("description", "文件描述")
.addFormDataPart("file", file.getName(),
RequestBody.create(file, MediaType.parse("application/octet-stream")))
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response);
}
return response.body().string();
}
}
}
4.7 配置超时和重试
import okhttp3.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class OkHttpTimeoutRetryExample {
public static void main(String[] args) {
// 配置超时
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
// 创建请求
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
// 使用重试拦截器
OkHttpClient clientWithRetry = client.newBuilder()
.addInterceptor(new RetryInterceptor(3))
.build();
try (Response response = clientWithRetry.newCall(request).execute()) {
System.out.println("Response Code: " + response.code());
System.out.println("Response Body: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
// 自定义重试拦截器
static class RetryInterceptor implements Interceptor {
private final int maxRetries;
public RetryInterceptor(int maxRetries) {
this.maxRetries = maxRetries;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
IOException exception = null;
Response response = null;
int retryCount = 0;
while (retryCount < maxRetries && (response == null || !response.isSuccessful())) {
if (retryCount > 0) {
System.out.println("重试请求: " + retryCount);
// 如果需要,可以在重试之间增加延迟
try {
Thread.sleep(1000 * retryCount);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Retry interrupted", e);
}
}
if (exception != null) {
System.out.println("上次尝试失败: " + exception.getMessage());
}
try {
response = chain.proceed(request);
} catch (IOException e) {
exception = e;
retryCount++;
if (retryCount >= maxRetries) {
throw exception;
}
continue;
}
if (!response.isSuccessful()) {
System.out.println("请求失败,状态码: " + response.code());
response.close();
retryCount++;
if (retryCount >= maxRetries) {
break;
}
}
}
return response;
}
}
}
4.8 配置 HTTPS 和 SSL
import okhttp3.*;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class OkHttpSslExample {
public static void main(String[] args) throws Exception {
// 方法1:信任所有证书(仅用于测试/开发环境!)
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
OkHttpClient unsafeClient = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
.hostnameVerifier((hostname, session) -> true)
.build();
// 方法2:使用自定义信任库和密钥库
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream trustStoreInputStream = OkHttpSslExample.class.getResourceAsStream("/truststore.jks")) {
trustStore.load(trustStoreInputStream, "truststorePassword".toCharArray());
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (InputStream keyStoreInputStream = OkHttpSslExample.class.getResourceAsStream("/keystore.p12")) {
keyStore.load(keyStoreInputStream, "keystorePassword".toCharArray());
}
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "keyPassword".toCharArray());
SSLContext clientAuthSslContext = SSLContext.getInstance("TLS");
clientAuthSslContext.init(
keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(),
null);
OkHttpClient secureClient = new OkHttpClient.Builder()
.sslSocketFactory(clientAuthSslContext.getSocketFactory(), (X509TrustManager) trustManagerFactory.getTrustManagers()[0])
.build();
// 创建请求
Request request = new Request.Builder()
.url("https://api.example.com/secure-data")
.build();
// 使用配置的客户端发送请求
try (Response response = unsafeClient.newCall(request).execute()) {
System.out.println("Response Code: " + response.code());
System.out.println("Response Body: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. Spring RestTemplate
Spring RestTemplate 是 Spring 框架提供的同步 HTTP 客户端,支持 RESTful 风格的 HTTP 交互。它是 Spring 应用中进行 HTTP 请求的经典选择,提供了丰富的方法来简化 HTTP 客户端的开发。
注意:从 Spring 5 开始,RestTemplate 被标记为处于维护模式,建议新项目使用 WebClient(下一节介绍)。
5.1 添加依赖
Maven:
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version>
</dependency>
Gradle:
implementation 'org.springframework:spring-web:5.3.23'
5.2 基本 GET 请求
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
public class RestTemplateGetExample {
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
// 基本 GET 请求(返回字符串)
String result = restTemplate.getForObject("https://api.example.com/data", String.class);
System.out.println("Response Body: " + result);
// 使用 ResponseEntity 获取更多响应细节
ResponseEntity<String> response = restTemplate.getForEntity("https://api.example.com/data", String.class);
System.out.println("Status Code: " + response.getStatusCode());
System.out.println("Headers: " + response.getHeaders());
System.out.println("Response Body: " + response.getBody());
}
}
5.3 获取并映射到 Java 对象
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
public class RestTemplateObjectMappingExample {
// 响应数据的模型类
static class User {
private Long id;
private String name;
private String email;
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
// 获取单个对象
User user = restTemplate.getForObject("https://api.example.com/users/1", User.class);
System.out.println("User: " + user);
// 获取对象数组
User[] users = restTemplate.getForObject("https://api.example.com/users", User[].class);
System.out.println("Users count: " + users.length);
for (User u : users) {
System.out.println(u);
}
// 使用 ResponseEntity 获取更多响应细节
ResponseEntity<User> response = restTemplate.getForEntity("https://api.example.com/users/1", User.class);
System.out.println("Status Code: " + response.getStatusCode());
System.out.println("User from response: " + response.getBody());
}
}
5.4 基本 POST 请求
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
public class RestTemplatePostExample {
// 请求数据的模型类
static class UserRequest {
private String name;
private String email;
public UserRequest(String name, String email) {
this.name = name;
this.email = email;
}
// Getters
public String getName() { return name; }
public String getEmail() { return email; }
}
// 响应数据的模型类
static class UserResponse {
private Long id;
private String name;
private String email;
private String createdAt;
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
@Override
public String toString() {
return "UserResponse{id=" + id + ", name='" + name + "', email='" + email + "', createdAt='" + createdAt + "'}";
}
}
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
// 创建请求对象
UserRequest requestUser = new UserRequest("John Doe", "john@example.com");
// 方法 1: 简单的 POST 请求
UserResponse createdUser = restTemplate.postForObject(
"https://api.example.com/users",
requestUser,
UserResponse.class);
System.out.println("Created User: " + createdUser);
// 方法 2: 使用 ResponseEntity 获取更多响应细节
ResponseEntity<UserResponse> response = restTemplate.postForEntity(
"https://api.example.com/users",
requestUser,
UserResponse.class);
System.out.println("Status Code: " + response.getStatusCode());
System.out.println("Created User from response: " + response.getBody());
// 方法 3: 使用 HttpEntity 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer your-token-here");
HttpEntity<UserRequest> requestEntity = new HttpEntity<>(requestUser, headers);
ResponseEntity<UserResponse> responseWithHeaders = restTemplate.postForEntity(
"https://api.example.com/users",
requestEntity,
UserResponse.class);
System.out.println("Status Code with headers: " + responseWithHeaders.getStatusCode());
System.out.println("Created User with headers: " + responseWithHeaders.getBody());
}
}
5.5 PUT 和 DELETE 请求
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
public class RestTemplatePutDeleteExample {
// 响应数据的模型类
static class User {
private Long id;
private String name;
private String email;
public User() {}
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
// PUT 请求 - 更新资源
User userToUpdate = new User(1L, "Updated Name", "updated@example.com");
// 方法 1: 简单的 PUT 请求
restTemplate.put("https://api.example.com/users/1", userToUpdate);
System.out.println("User updated");
// 方法 2: 使用 HttpEntity 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer your-token-here");
HttpEntity<User> requestEntity = new HttpEntity<>(userToUpdate, headers);
// 使用 exchange 方法可以获取响应
ResponseEntity<User> putResponse = restTemplate.exchange(
"https://api.example.com/users/1",
HttpMethod.PUT,
requestEntity,
User.class);
System.out.println("PUT Status Code: " + putResponse.getStatusCode());
System.out.println("Updated User: " + putResponse.getBody());
// DELETE 请求 - 删除资源
// 方法 1: 简单的 DELETE 请求
restTemplate.delete("https://api.example.com/users/2");
System.out.println("User deleted");
// 方法 2: 使用 exchange 方法带请求头并获取响应
HttpEntity<Void> deleteRequestEntity = new HttpEntity<>(headers);
ResponseEntity<Void> deleteResponse = restTemplate.exchange(
"https://api.example.com/users/3",
HttpMethod.DELETE,
deleteRequestEntity,
Void.class);
System.out.println("DELETE Status Code: " + deleteResponse.getStatusCode());
}
}
5.6 请求参数和路径变量
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
public class RestTemplateParamsExample {
// 响应数据的模型类
static class SearchResult {
private String query;
private int page;
private int perPage;
private int total;
private Object[] results;
// Getters and Setters
public String getQuery() { return query; }
public void setQuery(String query) { this.query = query; }
public int getPage() { return page; }
public void setPage(int page) { this.page = page; }
public int getPerPage() { return perPage; }
public void setPerPage(int perPage) { this.perPage = perPage; }
public int getTotal() { return total; }
public void setTotal(int total) { this.total = total; }
public Object[] getResults() { return results; }
public void setResults(Object[] results) { this.results = results; }
@Override
public String toString() {
return "SearchResult{query='" + query + "', page=" + page + ", perPage=" + perPage +
", total=" + total + ", results.length=" + (results != null ? results.length : 0) + "}";
}
}
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
// 1. 使用路径变量
String url = "https://api.example.com/users/{id}";
Map<String, String> params = new HashMap<>();
params.put("id", "123");
// 替换路径变量并发送请求
String result = restTemplate.getForObject(url, String.class, params);
System.out.println("User with ID 123: " + result);
// 或使用可变参数直接传递值
result = restTemplate.getForObject(url, String.class, "456");
System.out.println("User with ID 456: " + result);
// 2. 使用查询参数
// 方法 1: 使用 UriComponentsBuilder
URI uri = UriComponentsBuilder.fromHttpUrl("https://api.example.com/search")
.queryParam("q", "java")
.queryParam("page", 1)
.queryParam("per_page", 10)
.build()
.encode()
.toUri();
SearchResult searchResult = restTemplate.getForObject(uri, SearchResult.class);
System.out.println("Search Result: " + searchResult);
// 方法 2: 直接在 URL 中添加查询参数
String searchUrl = "https://api.example.com/search?q={q}&page={page}&per_page={perPage}";
Map<String, Object> searchParams = new HashMap<>();
searchParams.put("q", "spring");
searchParams.put("page", 2);
searchParams.put("perPage", 20);
SearchResult anotherResult = restTemplate.getForObject(searchUrl, SearchResult.class, searchParams);
System.out.println("Another Search Result: " + anotherResult);
}
}
5.7 处理响应错误
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
public class RestTemplateErrorHandlingExample {
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
try {
// 尝试获取不存在的资源
ResponseEntity<String> response = restTemplate.getForEntity(
"https://api.example.com/users/999",
String.class);
System.out.println("Status Code: " + response.getStatusCode());
System.out.println("Response Body: " + response.getBody());
} catch (HttpClientErrorException e) {
// 处理客户端错误 (4xx)
System.out.println("Client Error: " + e.getMessage());
System.out.println("Status Code: " + e.getStatusCode());
System.out.println("Response Body: " + e.getResponseBodyAsString());
// 根据状态码处理特定错误
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
System.out.println("资源不存在");
} else if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
System.out.println("认证失败,请检查凭证");
} else if (e.getStatusCode() == HttpStatus.FORBIDDEN) {
System.out.println("没有访问权限");
}
} catch (HttpServerErrorException e) {
// 处理服务器错误 (5xx)
System.out.println("Server Error: " + e.getMessage());
System.out.println("Status Code: " + e.getStatusCode());
System.out.println("Response Body: " + e.getResponseBodyAsString());
} catch (RestClientException e) {
// 处理其他 REST 客户端异常
System.out.println("REST Client Error: " + e.getMessage());
}
}
}
5.8 自定义 RestTemplate 配置
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.util.Timeout;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class RestTemplateCustomizationExample {
public static void main(String[] args) {
// 自定义 RestTemplate 配置
// 1. 配置连接池和超时
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(20);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.of(5, TimeUnit.SECONDS))
.setResponseTimeout(Timeout.of(5, TimeUnit.SECONDS))
.setConnectionRequestTimeout(Timeout.of(5, TimeUnit.SECONDS))
.build();
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
// 2. 自定义错误处理
ResponseErrorHandler customErrorHandler = new DefaultResponseErrorHandler() {
@Override
public boolean hasError(org.springframework.http.client.ClientHttpResponse response) throws IOException {
// 只在5xx状态码时认为是错误
return response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR;
}
};
// 3. 创建并配置 RestTemplate
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.setErrorHandler(customErrorHandler);
// 使用配置好的 RestTemplate
try {
String result = restTemplate.getForObject("https://api.example.com/data", String.class);
System.out.println("Response: " + result);
// 这里即使返回 4xx 也不会抛出异常
String notFound = restTemplate.getForObject("https://api.example.com/not-exists", String.class);
System.out.println("4xx Response: " + notFound);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
5.9 文件上传
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.io.File;
public class RestTemplateFileUploadExample {
public static void main(String[] args) {
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate();
// 准备文件资源
File file = new File("example.txt");
Resource fileResource = new FileSystemResource(file);
// 创建表单数据
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", fileResource);
body.add("description", "文件描述");
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// 创建请求实体
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
// 发送 POST 请求
ResponseEntity<String> response = restTemplate.postForEntity(
"https://api.example.com/upload",
requestEntity,
String.class);
// 输出结果
System.out.println("Status Code: " + response.getStatusCode());
System.out.println("Response: " + response.getBody());
}
}
5.10 请求和响应拦截器
import org.springframework.http.HttpRequest;
import org.springframework.http.client.*;
import org.springframework.web.client.RestTemplate;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class RestTemplateInterceptorExample {
// 请求拦截器
static class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
// 记录请求信息
System.out.println("===========================请求开始===========================");
System.out.println("URI: " + request.getURI());
System.out.println("Method: " + request.getMethod());
System.out.println("Headers: " + request.getHeaders());
System.out.println("Request Body: " + new String(body, StandardCharsets.UTF_8));
// 执行请求
ClientHttpResponse response = execution.execute(request, body);
// 创建可重复读取的响应包装器
BufferingClientHttpResponseWrapper responseWrapper =
new BufferingClientHttpResponseWrapper(response);
// 记录响应信息
System.out.println("===========================响应开始===========================");
System.out.println("Status Code: " + responseWrapper.getStatusCode());
System.out.println("Status Text: " + responseWrapper.getStatusText());
System.out.println("Headers: " + responseWrapper.getHeaders());
// 读取响应体
String responseBody = new BufferedReader(
new InputStreamReader(responseWrapper.getBody(), StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
System.out.println("Response Body: " + responseBody);
return responseWrapper;
}
}
// 响应包装器,使响应体可以重复读取
static class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
private final ClientHttpResponse response;
private byte[] body;
public BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
this.response = response;
}
@Override
public HttpStatus getStatusCode() throws IOException {
return response.getStatusCode();
}
@Override
public int getRawStatusCode() throws IOException {
return response.getRawStatusCode();
}
@Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
@Override
public void close() {
response.close();
}
@Override
public java.io.InputStream getBody() throws IOException {
if (body == null) {
body = response.getBody().readAllBytes();
}
return new java.io.ByteArrayInputStream(body);
}
@Override
public HttpHeaders getHeaders() {
return response.getHeaders();
}
}
public static void main(String[] args) {
// 创建客户端工厂
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(5000);
requestFactory.setReadTimeout(5000);
// 创建 RestTemplate 实例
RestTemplate restTemplate = new RestTemplate(requestFactory);
// 添加拦截器
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new LoggingRequestInterceptor());
restTemplate.setInterceptors(interceptors);
// 发送请求
String response = restTemplate.getForObject("https://api.example.com/data", String.class);
System.out.println("Final Response: " + response);
}
}
6. Spring WebClient
Spring WebClient 是 Spring 5 引入的非阻塞、响应式 HTTP 客户端,是 RestTemplate 的现代替代品。它基于 Project Reactor 构建,支持同步和异步操作,适合高性能和响应式编程场景。
6.1 添加依赖
Maven:
<!-- Spring WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.7.5</version>
</dependency>
Gradle:
implementation 'org.springframework.boot:spring-boot-starter-webflux:2.7.5'
6.2 基本 GET 请求
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class WebClientGetExample {
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 同步 GET 请求
String result = webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class)
.block(); // 阻塞等待结果
System.out.println("Synchronous Result: " + result);
// 异步 GET 请求
Mono<String> resultMono = webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class);
resultMono.subscribe(response -> {
System.out.println("Asynchronous Result: " + response);
});
// 等待异步操作完成(仅用于示例)
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6.3 获取并映射到 Java 对象
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class WebClientObjectMappingExample {
// 响应数据的模型类
static class User {
private Long id;
private String name;
private String email;
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 获取单个对象
User user = webClient.get()
.uri("/users/1")
.retrieve()
.bodyToMono(User.class)
.block(); // 阻塞等待结果
System.out.println("Single User: " + user);
// 获取对象列表
Flux<User> usersFlux = webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
// 处理对象流
usersFlux.subscribe(
u -> System.out.println("Received user: " + u),
error -> System.err.println("Error: " + error),
() -> System.out.println("Stream completed")
);
// 或者收集所有结果
usersFlux.collectList()
.subscribe(allUsers -> {
System.out.println("All users count: " + allUsers.size());
allUsers.forEach(System.out::println);
});
// 等待异步操作完成(仅用于示例)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6.4 基本 POST 请求
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class WebClientPostExample {
// 请求数据的模型类
static class UserRequest {
private String name;
private String email;
public UserRequest(String name, String email) {
this.name = name;
this.email = email;
}
// Getters
public String getName() { return name; }
public String getEmail() { return email; }
}
// 响应数据的模型类
static class UserResponse {
private Long id;
private String name;
private String email;
private String createdAt;
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
@Override
public String toString() {
return "UserResponse{id=" + id + ", name='" + name + "', email='" + email + "', createdAt='" + createdAt + "'}";
}
}
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 创建请求对象
UserRequest requestUser = new UserRequest("John Doe", "john@example.com");
// 同步 POST 请求
UserResponse createdUser = webClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestUser)
.retrieve()
.bodyToMono(UserResponse.class)
.block(); // 阻塞等待结果
System.out.println("Created User: " + createdUser);
// 异步 POST 请求
Mono<UserResponse> responseMono = webClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestUser)
.retrieve()
.bodyToMono(UserResponse.class);
responseMono.subscribe(
response -> System.out.println("Async Created User: " + response),
error -> System.err.println("Error: " + error)
);
// 等待异步操作完成(仅用于示例)
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6.5 PUT 和 DELETE 请求
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class WebClientPutDeleteExample {
// 数据模型类
static class User {
private Long id;
private String name;
private String email;
public User() {}
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// PUT 请求 - 更新资源
User userToUpdate = new User(1L, "Updated Name", "updated@example.com");
// 执行 PUT 请求并获取更新后的资源
User updatedUser = webClient.put()
.uri("/users/{id}", 1)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(userToUpdate)
.retrieve()
.bodyToMono(User.class)
.block(); // 阻塞等待结果
System.out.println("Updated User: " + updatedUser);
// DELETE 请求 - 删除资源
// 执行 DELETE 请求
webClient.delete()
.uri("/users/{id}", 2)
.retrieve()
.toBodilessEntity() // 获取无响应体的结果
.block(); // 阻塞等待结果
System.out.println("User with ID 2 deleted");
// 异步 DELETE 请求
Mono<Void> deleteMono = webClient.delete()
.uri("/users/{id}", 3)
.retrieve()
.bodyToMono(Void.class);
deleteMono.subscribe(
aVoid -> System.out.println("User with ID 3 deleted asynchronously"),
error -> System.err.println("Error deleting user: " + error)
);
// 等待异步操作完成(仅用于示例)
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6.6 请求参数和路径变量
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
public class WebClientParamsExample {
// 响应数据的模型类
static class SearchResult {
private String query;
private int page;
private int perPage;
private int total;
private Object[] results;
// Getters and Setters
public String getQuery() { return query; }
public void setQuery(String query) { this.query = query; }
public int getPage() { return page; }
public void setPage(int page) { this.page = page; }
public int getPerPage() { return perPage; }
public void setPerPage(int perPage) { this.perPage = perPage; }
public int getTotal() { return total; }
public void setTotal(int total) { this.total = total; }
public Object[] getResults() { return results; }
public void setResults(Object[] results) { this.results = results; }
@Override
public String toString() {
return "SearchResult{query='" + query + "', page=" + page + ", perPage=" + perPage +
", total=" + total + ", results.length=" + (results != null ? results.length : 0) + "}";
}
}
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 1. 使用路径变量
String userId = "123";
Mono<String> userMono = webClient.get()
.uri("/users/{id}", userId)
.retrieve()
.bodyToMono(String.class);
String user = userMono.block();
System.out.println("User with ID " + userId + ": " + user);
// 2. 使用多个路径变量
Mono<String> commentMono = webClient.get()
.uri("/users/{userId}/posts/{postId}/comments/{commentId}",
userId, "456", "789")
.retrieve()
.bodyToMono(String.class);
String comment = commentMono.block();
System.out.println("Comment: " + comment);
// 3. 使用 Map 传递路径变量
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("userId", "123");
uriVariables.put("postId", "456");
Mono<String> postMono = webClient.get()
.uri("/users/{userId}/posts/{postId}", uriVariables)
.retrieve()
.bodyToMono(String.class);
String post = postMono.block();
System.out.println("Post: " + post);
// 4. 添加查询参数
Mono<SearchResult> searchMono = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/search")
.queryParam("q", "java")
.queryParam("page", 1)
.queryParam("per_page", 10)
.build())
.retrieve()
.bodyToMono(SearchResult.class);
SearchResult searchResult = searchMono.block();
System.out.println("Search Result: " + searchResult);
}
}
6.7 处理错误
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
public class WebClientErrorHandlingExample {
// 自定义错误模型
static class ErrorResponse {
private String error;
private String message;
private int status;
// Getters and Setters
public String getError() { return error; }
public void setError(String error) { this.error = error; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
@Override
public String toString() {
return "ErrorResponse{error='" + error + "', message='" + message + "', status=" + status + "}";
}
}
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 方法 1: 使用 onStatus 处理特定状态码
Mono<String> resultMono = webClient.get()
.uri("/users/999") // 假设该资源不存在
.retrieve()
.onStatus(
HttpStatus::is4xxClientError,
response -> handleClientError(response)
)
.onStatus(
HttpStatus::is5xxServerError,
response -> Mono.error(new RuntimeException("服务器错误: " + response.statusCode()))
)
.bodyToMono(String.class);
try {
String result = resultMono.block();
System.out.println("Result: " + result);
} catch (Exception e) {
System.err.println("Error handled: " + e.getMessage());
}
// 方法 2: 使用 exchangeToMono 处理完整响应,包括错误
Mono<String> exchangeMono = webClient.get()
.uri("/users/999")
.exchangeToMono(response -> {
if (response.statusCode().is2xxSuccessful()) {
return response.bodyToMono(String.class);
} else if (response.statusCode().is4xxClientError()) {
return response.bodyToMono(ErrorResponse.class)
.flatMap(errorBody -> Mono.error(
new RuntimeException("Client error: " + errorBody)
));
} else {
return Mono.error(
new RuntimeException("Unexpected status: " + response.statusCode())
);
}
});
try {
String exchangeResult = exchangeMono.block();
System.out.println("Exchange Result: " + exchangeResult);
} catch (Exception e) {
System.err.println("Exchange error handled: " + e.getMessage());
}
// 方法 3: 使用全局错误处理并恢复
Mono<String> recoveryMono = webClient.get()
.uri("/users/999")
.retrieve()
.bodyToMono(String.class)
.onErrorResume(WebClientResponseException.class, e -> {
System.err.println("Error status code: " + e.getStatusCode());
System.err.println("Error body: " + e.getResponseBodyAsString());
// 提供默认值或执行恢复操作
return Mono.just("{ \"id\": -1, \"name\": \"默认用户\", \"email\": \"default@example.com\" }");
});
String recoveryResult = recoveryMono.block();
System.out.println("Recovery Result: " + recoveryResult);
}
private static Mono<? extends Throwable> handleClientError(ClientResponse response) {
if (response.statusCode().equals(HttpStatus.NOT_FOUND)) {
return Mono.error(new RuntimeException("资源不存在"));
} else if (response.statusCode().equals(HttpStatus.UNAUTHORIZED)) {
return Mono.error(new RuntimeException("认证失败,请检查凭证"));
} else if (response.statusCode().equals(HttpStatus.FORBIDDEN)) {
return Mono.error(new RuntimeException("没有访问权限"));
} else {
return response.bodyToMono(ErrorResponse.class)
.flatMap(errorBody -> Mono.error(
new RuntimeException("客户端错误: " + errorBody)
));
}
}
}
6.8 自定义 WebClient 配置
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import javax.net.ssl.SSLException;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class WebClientCustomizationExample {
public static void main(String[] args) throws SSLException {
// 1. 配置HTTP客户端
HttpClient httpClient = HttpClient.create()
// 连接超时
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
// 响应超时
.responseTimeout(Duration.ofSeconds(5))
// 读写超时
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS))
.addHandlerLast(new WriteTimeoutHandler(5, TimeUnit.SECONDS)))
// 配置SSL(允许自签名证书,仅用于测试)
.secure(spec -> spec.sslContext(
SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build()
));
// 2. 配置内存缓冲区大小
ExchangeStrategies strategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer
.defaultCodecs()
.maxInMemorySize(2 * 1024 * 1024)) // 2MB
.build();
// 3. 请求拦截器(添加统一请求头、日志等)
ExchangeFilterFunction requestFilter = ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
System.out.println("Request: " + clientRequest.method() + " " + clientRequest.url());
clientRequest.headers().forEach((name, values) ->
values.forEach(value -> System.out.println(name + ": " + value)));
return Mono.just(clientRequest);
});
// 4. 响应拦截器
ExchangeFilterFunction responseFilter = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
System.out.println("Response Status: " + clientResponse.statusCode());
clientResponse.headers().asHttpHeaders().forEach((name, values) ->
values.forEach(value -> System.out.println(name + ": " + value)));
return Mono.just(clientResponse);
});
// 5. 创建配置完成的 WebClient
WebClient webClient = WebClient.builder()
.baseUrl("https://api.example.com")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.exchangeStrategies(strategies)
.filter(requestFilter)
.filter(responseFilter)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.USER_AGENT, "WebClient-Example")
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
// 使用配置好的 WebClient
try {
String result = webClient.get()
.uri("/data")
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println("Result: " + result);
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
6.9 文件上传
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
public class WebClientFileUploadExample {
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 准备文件
Path path = Paths.get("example.txt");
File file = path.toFile();
// 方法 1: 使用 MultipartBodyBuilder
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
bodyBuilder.part("file", new FileSystemResource(file))
.filename(file.getName());
bodyBuilder.part("description", "文件描述");
String response = webClient.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))
.retrieve()
.bodyToMono(String.class)
.block();
System.out.println("Response: " + response);
// 方法 2: 直接使用 BodyInserters
Mono<String> responseMono = webClient.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData("file", new FileSystemResource(file))
.with("description", "另一个文件描述"))
.retrieve()
.bodyToMono(String.class);
String anotherResponse = responseMono.block();
System.out.println("Another Response: " + anotherResponse);
}
}
6.10 同步与异步并行请求
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
public class WebClientParallelExample {
// 响应数据的模型类
static class User {
private Long id;
private String name;
private String email;
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
public static void main(String[] args) {
// 创建 WebClient 实例
WebClient webClient = WebClient.create("https://api.example.com");
// 1. 顺序请求
User user1 = webClient.get()
.uri("/users/1")
.retrieve()
.bodyToMono(User.class)
.block();
User user2 = webClient.get()
.uri("/users/2")
.retrieve()
.bodyToMono(User.class)
.block();
System.out.println("Sequential User 1: " + user1);
System.out.println("Sequential User 2: " + user2);
// 2. 并行请求
List<Integer> userIds = Arrays.asList(1, 2, 3, 4, 5);
List<User> users = Flux.fromIterable(userIds)
.flatMap(id -> webClient.get()
.uri("/users/{id}", id)
.retrieve()
.bodyToMono(User.class)
)
.collectList()
.block();
System.out.println("Parallel Users count: " + users.size());
users.forEach(System.out::println);
// 3. 并行请求并合并结果
Mono<User> userMono = webClient.get()
.uri("/users/1")
.retrieve()
.bodyToMono(User.class);
Mono<String> postsMono = webClient.get()
.uri("/users/1/posts")
.retrieve()
.bodyToMono(String.class);
// 合并两个请求的结果
Mono<String> combined = Mono.zip(userMono, postsMono, (user, posts) -> {
return "User: " + user + ", Posts: " + posts;
});
String combinedResult = combined.block();
System.out.println("Combined Result: " + combinedResult);
// 4. 串行依赖请求
String nestedResult = webClient.get()
.uri("/users/1")
.retrieve()
.bodyToMono(User.class)
.flatMap(user -> {
// 使用第一个请求的结果来构建第二个请求
return webClient.get()
.uri("/users/{id}/details", user.getId())
.retrieve()
.bodyToMono(String.class)
.map(details -> "User: " + user + ", Details: " + details);
})
.block();
System.out.println("Nested Result: " + nestedResult);
}
}
7. Retrofit
Retrofit 是由 Square 公司开发的类型安全的 HTTP 客户端,专为 Android 和 Java 设计。它将 HTTP API 转换为 Java 接口,简化了 API 调用的实现。Retrofit 使用注解来描述 HTTP 请求,内部使用 OkHttp 处理 HTTP 请求。
7.1 添加依赖
Maven:
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.9.0</version>
</dependency>
Gradle:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
7.2 定义 API 接口
import retrofit2.Call;
import retrofit2.http.*;
import java.util.List;
import java.util.Map;
/**
* 定义 API 接口
*/
public interface ApiService {
// GET 请求
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
// 带查询参数的 GET 请求
@GET("users")
Call<List<User>> getUsers(@Query("page") int page, @Query("limit") int limit);
// 动态 URL 的 GET 请求
@GET
Call<List<User>> getUsersWithDynamicUrl(@Url String url);
// POST 请求
@POST("users")
Call<User> createUser(@Body User user);
// 表单数据的 POST 请求
@FormUrlEncoded
@POST("login")
Call<LoginResponse> login(
@Field("username") String username,
@Field("password") String password);
// 多部分表单数据 (文件上传)
@Multipart
@POST("upload")
Call<UploadResponse> uploadFile(
@Part("description") String description,
@Part MultipartBody.Part file);
// PUT 请求
@PUT("users/{id}")
Call<User> updateUser(@Path("id") int userId, @Body User user);
// PATCH 请求
@PATCH("users/{id}")
Call<User> patchUser(@Path("id") int userId, @Body Map<String, Object> updates);
// DELETE 请求
@DELETE("users/{id}")
Call<Void> deleteUser(@Path("id") int userId);
// 添加请求头
@GET("secure-data")
Call<SecureData> getSecureData(@Header("Authorization") String authToken);
// 添加固定请求头
@Headers({
"Accept: application/json",
"User-Agent: Retrofit-Sample"
})
@GET("users")
Call<List<User>> getUsersWithHeaders();
}
7.3 数据模型
/**
* 用户模型
*/
public class User {
private int id;
private String name;
private String email;
// 构造函数
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
}
}
/**
* 登录响应模型
*/
public class LoginResponse {
private String token;
private String refreshToken;
private User user;
// Getters and Setters
public String getToken() { return token; }
public void setToken(String token) { this.token = token; }
public String getRefreshToken() { return refreshToken; }
public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; }
public User getUser() { return user; }
public void setUser(User user) { this.user = user; }
@Override
public String toString() {
return "LoginResponse{token='" + token + "', refreshToken='" + refreshToken + "', user=" + user + "}";
}
}
/**
* 上传响应模型
*/
public class UploadResponse {
private String fileUrl;
private long fileSize;
private String fileType;
// Getters and Setters
public String getFileUrl() { return fileUrl; }
public void setFileUrl(String fileUrl) { this.fileUrl = fileUrl; }
public long getFileSize() { return fileSize; }
public void setFileSize(long fileSize) { this.fileSize = fileSize; }
public String getFileType() { return fileType; }
public void setFileType(String fileType) { this.fileType = fileType; }
@Override
public String toString() {
return "UploadResponse{fileUrl='" + fileUrl + "', fileSize=" + fileSize + ", fileType='" + fileType + "'}";
}
}
/**
* 安全数据模型
*/
public class SecureData {
private String data;
private long timestamp;
// Getters and Setters
public String getData() { return data; }
public void setData(String data) { this.data = data; }
public long getTimestamp() { return timestamp; }
public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
@Override
public String toString() {
return "SecureData{data='" + data + "', timestamp=" + timestamp + "}";
}
}
7.4 创建 Retrofit 实例
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import java.util.concurrent.TimeUnit;
public class RetrofitClient {
private static final String BASE_URL = "https://api.example.com/";
private static Retrofit retrofit = null;
/**
* 获取 Retrofit 实例
*/
public static Retrofit getClient() {
if (retrofit == null) {
// 创建 OkHttpClient 并配置
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
// 创建 Gson 实例
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
// 创建 Retrofit 实例
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
/**
* 获取 API 服务
*/
public static ApiService getApiService() {
return getClient().create(ApiService.class);
}
}
7.5 基本 GET 请求
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.IOException;
import java.util.List;
public class RetrofitGetExample {
public static void main(String[] args) {
// 获取 API 服务
ApiService apiService = RetrofitClient.getApiService();
// 同步 GET 请求
try {
Response<User> response = apiService.getUser(1).execute();
if (response.isSuccessful()) {
User user = response.body();
System.out.println("User: " + user);
} else {
System.err.println("Error: " + response.code() + " " + response.message());
}
} catch (IOException e) {
e.printStackTrace();
}
// 异步 GET 请求
apiService.getUsers(1, 10).enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful()) {
List<User> users = response.body();
System.out.println("Users count: " + users.size());
for (User user : users) {
System.out.println(user);
}
} else {
System.err.println("Error: " + response.code() + " " + response.message());
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
System.err.println("Request failed: " + t.getMessage());
}
});
// 等待异步请求完成(仅用于示例)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
7.6 基本 POST 请求
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.IOException;
public class RetrofitPostExample {
public static void main(String[] args) {
// 获取 API 服务
ApiService apiService = RetrofitClient.getApiService();
// 创建用户对象
User newUser = new User("John Doe", "john@example.com");
// 同步 POST 请求
try {
Response<User> response = apiService.createUser(newUser).execute();
if (response.isSuccessful()) {
User createdUser = response.body();
System.out.println("Created User: " + createdUser);
} else {
System.err.println("Error: " + response.code() + " " + response.message());
}
} catch (IOException e) {
e.printStackTrace();
}
// 异步 POST 请求
User anotherUser = new User("Jane Smith", "jane@example.com");
apiService.createUser(anotherUser).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
User createdUser = response.body();
System.out.println("Created Another User: " + createdUser);
} else {
System.err.println("Error: " + response.code() + " " + response.message());
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
System.err.println("Request failed: " + t.getMessage());
}
});
// 表单提交
apiService.login("john_doe", "password123").enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.isSuccessful()) {
LoginResponse loginResponse = response.body();
System.out.println("Login successful: " + loginResponse);
} else {
System.err.println("Login failed: " + response.code() + " " + response.message());
}
}
@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
System.err.println("Login request failed: " + t.getMessage());
}
});
// 等待异步请求完成(仅用于示例)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
7.7 文件上传
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.File;
public class RetrofitFileUploadExample {
public static void main(String[] args) {
// 获取 API 服务
ApiService apiService = RetrofitClient.getApiService();
// 准备文件
File file = new File("example.txt");
// 创建 RequestBody
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
// MultipartBody.Part 包装以表单方式上传
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
// 添加描述
String description = "文件描述";
// 异步上传
apiService.uploadFile(description, filePart).enqueue(new Callback<UploadResponse>() {
@Override
public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) {
if (response.isSuccessful()) {
UploadResponse uploadResponse = response.body();
System.out.println("Upload successful: " + uploadResponse);
} else {
System.err.println("Upload failed: " + response.code() + " " + response.message());
}
}
@Override
public void onFailure(Call<UploadResponse> call, Throwable t) {
System.err.println("Upload request failed: " + t.getMessage());
}
});
// 等待异步请求完成(仅用于示例)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
7.8 自定义 OkHttpClient
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class RetrofitCustomOkHttpExample {
private static final String BASE_URL = "https://api.example.com/";
private static final String AUTH_TOKEN = "your-auth-token";
public static void main(String[] args) {
// 创建日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 创建认证拦截器
Interceptor authInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// 添加认证头
Request authenticatedRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer " + AUTH_TOKEN)
.build();
return chain.proceed(authenticatedRequest);
}
};
// 创建重试拦截器
Interceptor retryInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 最大重试次数
int maxRetries = 3;
int retryCount = 0;
Response response = null;
IOException ioException = null;
while (retryCount < maxRetries && (response == null || !response.isSuccessful())) {
if (retryCount > 0) {
System.out.println("重试请求 #" + retryCount + ": " + request.url());
}
if (ioException != null) {
System.out.println("上次请求失败: " + ioException.getMessage());
}
try {
response = chain.proceed(request);
} catch (IOException e) {
ioException = e;
retryCount++;
if (retryCount >= maxRetries) {
throw ioException;
}
continue;
}
// 当服务器错误时重试
if (!response.isSuccessful() && response.code() >= 500) {
retryCount++;
if (retryCount >= maxRetries) {
break;
}
response.close();
} else {
break;
}
}
return response;
}
};
// 创建 OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(authInterceptor) // 认证拦截器
.addInterceptor(retryInterceptor) // 重试拦截器
.addInterceptor(loggingInterceptor) // 日志拦截器
.build();
// 创建 Retrofit 实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
// 创建 API 服务
ApiService apiService = retrofit.create(ApiService.class);
// 使用配置好的 API 服务发送请求
apiService.getSecureData("Bearer " + AUTH_TOKEN).enqueue(new retrofit2.Callback<SecureData>() {
@Override
public void onResponse(retrofit2.Call<SecureData> call, retrofit2.Response<SecureData> response) {
if (response.isSuccessful()) {
SecureData secureData = response.body();
System.out.println("Secure Data: " + secureData);
} else {
System.err.println("Error: " + response.code() + " " + response.message());
}
}
@Override
public void onFailure(retrofit2.Call<SecureData> call, Throwable t) {
System.err.println("Request failed: " + t.getMessage());
}
});
// 等待异步请求完成(仅用于示例)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
8. 小结与对比
8.1 各个 HTTP 客户端库的特点
客户端库 | 特点 | 优势 | 适用场景 |
---|---|---|---|
Java 原生 HttpURLConnection | 标准库内置,无需额外依赖 | 简单,轻量 | 简单的 HTTP 请求,对依赖项敏感的项目 |
Java 11+ HttpClient | Java 标准库内置,现代 API | 支持异步,WebSocket,HTTP/2 | Java 11+ 的现代项目,需要现代特性但不想引入外部依赖 |
Apache HttpClient | 功能丰富,成熟稳定 | 连接池管理,丰富的配置选项 | 需要高度定制化和精细控制的复杂 HTTP 交互 |
OkHttp | 高效,现代化,专注于性能 | HTTP/2 支持,连接池,拦截器 | 对性能要求高的应用,尤其是 Android 应用 |
Spring RestTemplate | Spring 生态集成,简洁 API | 与 Spring 无缝集成,消息转换器 | Spring 应用,特别是传统的同步编程模型 |
Spring WebClient | 非阻塞,响应式 | 支持响应式编程模型,背压控制 | 响应式 Spring 应用,高并发场景 |
Retrofit | 类型安全,接口驱动 | 简洁的 API 定义,易于维护 | API 客户端开发,尤其是结构化良好的 RESTful API |
8.2 如何选择合适的 HTTP 客户端库
考虑项目约束:
- 如果项目不能引入外部依赖,选择 Java 原生 HttpURLConnection 或 Java 11+ HttpClient
- 如果已经使用 Spring 框架,优先考虑 RestTemplate 或 WebClient
考虑编程模型:
- 同步编程模型: HttpURLConnection, Apache HttpClient, RestTemplate, OkHttp
- 响应式/异步编程模型: Java 11+ HttpClient, WebClient, OkHttp
考虑功能需求:
- 简单 HTTP 请求: 任何客户端都可以
- 连接池和高级配置: Apache HttpClient, OkHttp
- 类型安全的 API 客户端: Retrofit
- 响应式编程: WebClient
考虑性能要求:
- 高并发: WebClient, OkHttp
- 低延迟: OkHttp
- 内存效率: Apache HttpClient, OkHttp
考虑可维护性:
- 代码简洁度: Retrofit, WebClient
- 调试便利性: Apache HttpClient (详细日志)
- API 定义清晰度: Retrofit
考虑团队经验:
- 选择团队成员熟悉的技术,降低学习成本
8.3 最佳实践建议
常规 Java 应用:
- Java 11+: 优先使用 Java 11+ HttpClient
- Java 8 或更早: 考虑 OkHttp 或 Apache HttpClient
Spring 应用:
- Spring 5+,响应式编程: WebClient
- 传统 Spring MVC: RestTemplate
- 注意 RestTemplate 已进入维护模式,新项目建议使用 WebClient
Android 应用:
- 优先考虑 OkHttp 或基于 OkHttp 的 Retrofit
微服务架构:
- 服务间通信: WebClient 或 Retrofit
- 支持熔断等弹性功能: 结合 Resilience4j
高性能要求:
- 使用非阻塞客户端: WebClient
- 配置适当的连接池和超时
安全最佳实践:
- 始终验证 HTTPS 证书(除开发环境外)
- 设置合理的连接和读取超时
- 实施重试机制但要避免大量重试导致的级联故障
- 处理所有异常并记录有用的错误信息