在现有项目中,我有一个 HTTP 服务,它使用 Apache 的 HttpClient 4 获取数据并返回响应
InputStream
,如以下代码示例所示:
public class HttpClient4Demo {
public static void main(String[] args) throws IOException {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
try (Scanner scanner = new Scanner(fetchData(httpClient))) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
}
}
}
private static InputStream fetchData(HttpClient httpClient) throws IOException {
HttpResponse response = httpClient.execute(new HttpGet(
"https://dummyjson.com/products/1"
));
return response.getEntity().getContent();
}
}
我想将该服务迁移到 Apache 的 HttpClient 5,所以我必须重写代码:
public class HttpClient5Demo {
public static void main(String[] args) throws IOException {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
try (Scanner scanner = new Scanner(fetchData(httpClient))) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
}
}
}
private static InputStream fetchData(HttpClient httpClient) throws IOException {
ClassicHttpResponse response = httpClient.executeOpen(
null,
new HttpGet("https://dummyjson.com/products/1"),
null
);
return response.getEntity().getContent();
}
}
但是该代码给了我一个警告,因为使用
ClassicHttpResponse
时没有使用 try-with-resources。
如果我用 try-with-resources 包裹
ClassicHttpResponse
,我会得到一个关闭的 InputStream
,所以我将无法读取响应数据。
private static InputStream fetchData(HttpClient httpClient) throws IOException {
try (ClassicHttpResponse response = httpClient.executeOpen(
null,
new HttpGet("https://dummyjson.com/products/1"),
null
)) {
return response.getEntity().getContent();
}
}
我还可以将
response.getEntity()
包装到 ByteArrayInputStream
中,我将得到一个有效的 InputStream
,但这不是一个完美的解决方案,因为整个响应数据将存储在 RAM 中。
private static InputStream fetchData(HttpClient httpClient) throws IOException {
try (ClassicHttpResponse response = httpClient.executeOpen(
null,
new HttpGet("https://dummyjson.com/products/1"),
null
)) {
return new ByteArrayInputStream(EntityUtils.toByteArray(response.getEntity()));
}
}
那么有什么方法可以在不关闭响应和/或使用 HttpClient 5 将其存储在 RAM 中的情况下获得响应
InputStream
?
要消除警告,请返回
ClassicHttpResponse
而不是 InputStream
,并将其放入 try-with-resources 子句中,如下所示。
public class HttpClient5Demo {
public static void main(String[] args) throws IOException {
try (CloseableHttpClient httpClient = HttpClients.createDefault();
ClassicHttpResponse response = fetchData(httpClient);
Scanner scanner = new Scanner(response.getEntity().getContent())) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
}
}
private static ClassicHttpResponse fetchData(HttpClient httpClient) throws IOException {
return httpClient.executeOpen(null, new HttpGet("https://dummyjson.com/products/1"), null);
}
}
由于关闭
ClassicHttpResponse
相当于关闭InputStream
,如果返回后能确保InputStream
正确关闭,则该警告也可以忽略。
你可能会觉得有点不舒服,所以才来到推荐的方式。
检查HttpClient#executeOpen,这实际上是在调用已弃用的方法HttpClient#execute。
强烈建议使用 HttpClientResponseHandler 的执行方法,例如execute(HttpHost, ClassicHttpRequest, HttpClientResponseHandler),以确保客户端自动释放资源。对于特殊情况,仍然可以使用executeOpen(HttpHost, ClassicHttpRequest, HttpContext) 在请求执行后保持响应对象打开。
我们可以参考示例演示了处理HTTP响应的推荐方式,处理过程如下
private static void printData(HttpClient httpClient) throws IOException {
httpClient.execute(new HttpGet("https://dummyjson.com/products/1"), (response) -> {
try (Scanner scanner = new Scanner(response.getEntity().getContent())) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
}
return null;
});
}
return null
这里看起来有点奇怪,但在实际场景中,我们通常将响应映射到另一个对象并返回它。