tulip notes
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Star-Lord

希望一天成为大师的学徒
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 探索SpringBoot

  • 常用功能实现

    • SpringBoot常用功能实现_1
    • SpringBoot中拦截器与日志
    • 多环境配置与数据绑定
    • 异步方法(线程池)的实现
    • controller参数接收
    • SpringBoot中关于日志的更好使用
    • 异常捕获的一些细节
    • 时间跟其他数据的序列化
    • 面向切面跟自定义注解的结合
    • 集成Redis及注意事项
    • springboot中发送http
      • 前言
      • SpringBoot中使用http
        • HttpClient(Apache HttpClient)
        • 引入坐标
        • 发送Get请求
        • 另一个Get请求例子:
        • 资源的关闭
        • 必须关闭 CloseableHttpResponse
        • 关闭 CloseableHttpClient
        • 不关闭资源的后果
        • 关闭资源与被动关闭
        • 发送Post请求
        • 总结与补充
        • OKHttp
        • 发送Get请求
        • 发送Post请求
        • 总结与补充
        • 异步请求
        • Apache HttpClient 实现异步请求
        • OkHttp 实现异步请求
        • WebClient
        • 发送Get请求
        • 发送Post请求
        • 总结与补充
        • 方案对比
  • Security认证授权

  • 扩展

  • 实战与注意事项

  • 其它

  • 《SpringBoot》笔记
  • 常用功能实现
EffectTang
2025-06-17
目录

springboot中发送http

# SpringBoot中发送http

# 前言

我们编写的应用,时不时的会跟外部系统进行交互(某些应用经常要与其他系统进行交互),目前主要的方式有http,或者rpc...,所以学会在应用中发送http请求,或rpc请求是十分重要的。

而http,在其中又是最最常用的,所以熟练掌握它,对一个开发者来说,可以说是必修课了。下面就一起来看下,在SpringBoot应用中如何发送http请求,以及一些注意事项,还有跟外部系统进行交互时,要注意的一些点。

# SpringBoot中使用http

在 Spring Boot 应用中,发送 HTTP 请求有多种方式,以下是常见的几种方法及其使用流程和示例:

# HttpClient(Apache HttpClient)

HttpClient 是 Apache 提供的一个强大的 HTTP 客户端库,支持同步和异步请求,功能丰富。

# 引入坐标

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.2.1</version>
</dependency>
1
2
3
4
5

HttpClient的核心API:

  • HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
  • HttpClients:可认为是构建器,可创建HttpClient对象。
  • CloseableHttpClient:实现类,实现了HttpClient接口。
  • HttpGet:Get方式请求类型。
  • HttpPost:Post方式请求类型。

HttpClient发送请求步骤:

  • 创建HttpClient对象
  • 创建Http请求对象
  • 调用HttpClient的execute方法发送请求

# 发送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.HttpClients;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.io.entity.EntityUtils;

public class HttpClientExample {
    public static void main(String[] args) throws Exception {
        // 创建 HttpClient 实例
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 创建 GET 请求
            HttpGet request = new HttpGet("https://api.example.com/data");

            // 发送请求并获取响应
            ClassicHttpResponse response = httpClient.execute(request);
			
            // 处理响应
            System.out.println("Status Code: " + response.getCode());
            System.out.println("Response Body: " + EntityUtils.toString(response.getEntity()));
          
            //获取服务端返回的状态码
            int statusCode = response.getStatusLine().getStatusCode();
            System.out.println("服务端返回的状态码为:" + statusCode);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 另一个Get请求例子:

@Test
public void testGET() throws Exception{
    //创建httpclient对象
    CloseableHttpClient httpClient = HttpClients.createDefault();

    //创建请求对象
    HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");

    //发送请求,接受响应结果
    CloseableHttpResponse response = httpClient.execute(httpGet);

    //获取服务端返回的状态码
    int statusCode = response.getStatusLine().getStatusCode();
    System.out.println("服务端返回的状态码为:" + statusCode);

    HttpEntity entity = response.getEntity();
    String body = EntityUtils.toString(entity);
    System.out.println("服务端返回的数据为:" + body);

    //关闭资源
    response.close();
    httpClient.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 资源的关闭

上述2个例子,存在一个很大的不同,就是————对资源的关闭。

在 Apache HttpClient 中,未正确关闭 CloseableHttpResponse 和 CloseableHttpClient 会导致严重的资源泄漏问题,具体原因如下:

# 必须关闭 CloseableHttpResponse

为什么必须关闭——CloseableHttpResponse,主要是因为以下2个原因:

  • 连接池泄漏: CloseableHttpResponse 的 close() 方法会将当前使用的 HTTP 连接释放回连接池(如果使用了连接池,如 PoolingHttpClientConnectionManager)。如果不关闭 response,连接池中的连接会被一直占用,最终导致连接池耗尽,后续请求无法获取连接,抛出类似 ConnectionPoolTimeoutException 的错误。
  • 资源释放: close() 方法会关闭底层的网络 socket 和输入流,释放操作系统资源(如文件句柄、内存)。如果不关闭,会导致内存泄漏和系统资源耗尽。

# 关闭 CloseableHttpClient

  • 连接池关闭: CloseableHttpClient 的 close() 方法会关闭连接池中的所有连接,并释放相关资源。如果 HttpClient 是全局单例的(推荐方式),通常不需要手动关闭它,但如果是临时创建的客户端,不关闭会导致连接池无法释放,占用内存和网络资源。
  • 避免重复关闭: 如果 HttpClient 是单例的,频繁调用 close() 会导致异常(例如 IllegalStateException: Connection pool shut down)。

# 不关闭资源的后果

  1. 连接池耗尽: 所有连接都被占用,后续请求阻塞或失败。
  2. 内存泄漏: 未关闭的 socket 和流会导致内存占用持续增长。
  3. 系统资源耗尽: 文件句柄、线程等资源被耗尽,最终导致应用崩溃。

# 关闭资源与被动关闭

了解资源关闭的必要性后,我们继续看上述2个例子中的最大不同————资源的关闭。

第一个是被动关闭,但它没关闭httpclient。

第二个,则是主动关闭的,且它Response和httpclient都关闭的。

第一个的主动关闭,是通过jdk7推出的————try-with-resources。以下是一个代码示例:

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.HttpClients;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.io.entity.EntityUtils;

public class HttpClientExample {
    public static void main(String[] args) throws Exception {
        // 创建临时 HttpClient 实例
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet("https://api.example.com/data");

            // 使用 try-with-resources 自动关闭 response
            try (ClassicHttpResponse response = httpClient.execute(request)) {
                // 处理响应
                System.out.println("Status Code: " + response.getCode());
                System.out.println("Response Body: " + EntityUtils.toString(response.getEntity()));
            } // response 在此处自动关闭
        } // httpClient 在此处自动关闭
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

上述例子中的 try (xxx) { ... } 语法就是 Java 7 引入的 try-with-resources 模式(也称为自动资源管理)。它的核心功能是 自动关闭在 try() 括号中声明的资源,前提是这些资源实现了 AutoCloseable 接口(或其子接口 Closeable)。

try-with-resources 的工作原理

  • 资源声明:在 try() 括号中声明并初始化一个或多个资源(如文件流、数据库连接、网络连接等)。
  • 自动关闭:无论 try 块是否正常执行完毕,还是抛出异常,Java 会 自动按声明的逆序关闭资源。
  • 无需手动调用 close():资源的 close() 方法会在 try 块结束后由 JVM 自动调用。

注意事项:

  • 不仅Get形式的请求需要关闭资源,Post形式的请求也要关闭资源。

  • 资源必须实现 AutoCloseable:否则无法使用 try-with-resources。

# 发送Post请求

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.HttpClients;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicNameValuePair;

import java.util.ArrayList;
import java.util.List;

public class HttpClientPostExample {
    public static void main(String[] args) throws Exception {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            URIBuilder uriBuilder = new URIBuilder("https://api.example.com/submit");
            // 添加查询参数
            uriBuilder.addParameter("param1", "value1");

            HttpPost request = new HttpPost(uriBuilder.build());

            // 设置请求体(表单数据)
            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("username", "user123"));
            params.add(new BasicNameValuePair("password", "pass123"));
            request.setEntity(new UrlEncodedFormEntity(params));

            // 发送请求
            ClassicHttpResponse response = httpClient.execute(request);

            // 处理响应
            System.out.println("Status Code: " + response.getCode());
            System.out.println("Response Body: " + EntityUtils.toString(response.getEntity()));
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 总结与补充

优点:

  • 功能强大,支持复杂的请求配置(如超时、代理、重试等)。
  • 社区成熟,文档丰富。

缺点:

  • API 相对繁琐,需要手动处理更多细节。

上述的几个例子都是同步请求,如果使用httpclient发送异步请求,如何实现呢?

# OKHttp

OkHttp 是 Square 开发的现代 HTTP 客户端,API 更简洁,支持同步和异步请求。

# 发送Get请求

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class OkHttpExample {
    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("Status Code: " + response.code());
            System.out.println("Response Body: " + response.body().string());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 发送Post请求

import okhttp3.*;

import java.io.IOException;

public class OkHttpPostExample {
    public static void main(String[] args) throws IOException {
        OkHttpClient client = new OkHttpClient();

        MediaType JSON = MediaType.get("application/json; charset=utf-8");
        String jsonBody = "{\"username\":\"user123\",\"password\":\"pass123\"}";
        RequestBody body = RequestBody.create(jsonBody, JSON);

        Request request = new Request.Builder()
                .url("https://api.example.com/submit")
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("Status Code: " + response.code());
            System.out.println("Response Body: " + response.body().string());
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 总结与补充

优点:

  • API 简洁,代码量少。
  • 支持异步请求和连接池管理。
  • 自动处理 GZIP 压缩、缓存等。

缺点:

  • 需要额外引入依赖。
  • 对于复杂请求(如文件上传)需要更多配置。

# 异步请求

之前介绍了httpclient和okhttp,但都只介绍了同步请求,也就是请求后,需要等对方反馈后,即拿到响应后,才继续执行后续的代码。

既然它们2个都支持这种异步请求,那么该如何发送呢?

再介绍之前,再次说明下——异步请求。

异步请求指的是客户端发送请求后,不必等待服务器响应就可以继续执行其他任务。一旦服务器返回结果,会通过回调或者Future等方式通知客户端处理结果。这种方式特别适合于需要长时间等待的服务调用场景,可以有效提高程序的效率和响应速度。

# Apache HttpClient 实现异步请求

从 Apache HttpClient 5.x 开始,官方支持了异步请求的功能。它使用 HttpAsyncClient 来实现异步请求,并依赖于回调机制来处理响应。

import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.core5.concurrent.FutureCallback;

public class AsyncHttpClientExample {

    public static void main(String[] args) throws Exception {
        // 创建异步HTTP客户端实例
        try (CloseableHttpAsyncClient client = HttpAsyncClients.createDefault()) {
          
            // 启动客户端
           // 启动后,客户端会初始化工作线程(IOReactor),用于处理异步请求和响应。
            client.start();

            // 创建GET请求
            SimpleHttpRequest request = SimpleHttpRequest.get("https://api.example.com/data");

            // 执行请求并设置回调
           // 提交异步请求,并注册一个回调对象 FutureCallback
            client.execute(request, new FutureCallback<SimpleHttpResponse>() {
              
              	// 请求成功时调用,参数 response 是响应对象
                @Override
                public void completed(SimpleHttpResponse response) {
                    System.out.println("Request completed: " + response);
                }

                @Override
                public void failed(Exception ex) {
                    System.err.println("Request failed: " + ex.getMessage());
                }
							 // 请求被取消时调用。
                @Override
                public void cancelled() {
                    System.out.println("Request cancelled");
                }
              // 调用 .get() 会阻塞当前线程,直到请求完成,违背了异步请求的初衷。
            }).get(); // 使用 .get() 等待请求完成(仅用于演示)
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

注意:在实际应用中,你可能不需要调用 .get() 方法,因为它会使异步调用变成同步。这里只是为了演示目的而添加。

此外,你还要注意,main方法的局限性:

方法是静态的,如果直接注册回调,主线程可能在回调触发前退出。解决方法:

  1. 在回调中启动新线程或等待(例如 Thread.sleep(...))。
  2. 将异步逻辑封装到非静态方法中。

# OkHttp 实现异步请求

OkHttp 原生支持异步请求,使用 enqueue 方法进行异步调用,并且通过 Callback 接口处理响应。

import okhttp3.*;

import java.io.IOException;

public class AsyncOkHttpExample {
    OkHttpClient client = new OkHttpClient();

    public static void main(String[] args) {
        AsyncOkHttpExample example = new AsyncOkHttpExample();
        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .build();
        example.run(request);
    }

    public void run(Request request) {
       // 以 异步方式 提交请求,并注册回调处理响应。
        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 {
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

                System.out.println(response.body().string());
            }
        });
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  • enqueue(Callback callback):以异步方式提交请求,并注册回调处理响应。
    • 异步特性:请求在后台线程执行,不会阻塞主线程。
    • 回调机制:通过 Callback 接口的两个方法处理响应结果。

核心流程如下:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://api.example.com/data").build();
client.newCall(request).enqueue(new Callback() { ... });
1
2
3
  • OkHttp 对异步请求的支持更加直接,利用 enqueue 方法配合 Callback 接口即可轻松实现异步请求处理。

在选择时,可以根据项目需求以及对库的熟悉程度来决定使用哪一个。如果项目中已经大量使用了某个库,通常建议保持一致性以减少维护成本。

# WebClient

WebClient 是 Spring 5 推出的非阻塞、响应式 HTTP 客户端,适合高并发场景。

# 发送Get请求

import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class WebClientExample {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create();

        Mono<String> responseMono = webClient.get()
                .uri("https://api.example.com/data")
                .retrieve()
                .bodyToMono(String.class);

        String response = responseMono.block(); // 阻塞获取结果
        System.out.println("Response: " + response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 发送Post请求

import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class WebClientPostExample {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create();

        String jsonBody = "{\"username\":\"user123\",\"password\":\"pass123\"}";

        Mono<String> responseMono = webClient.post()
                .uri("https://api.example.com/submit")
                .header("Content-Type", "application/json")
                .bodyValue(jsonBody)
                .retrieve()
                .bodyToMono(String.class);

        String response = responseMono.block();
        System.out.println("Response: " + response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 总结与补充

优点:

  • 非阻塞式,适合高并发场景。
  • 支持响应式编程(Reactive Streams)。

缺点:

  • 学习曲线较高(需要了解响应式编程概念)。

# 方案对比

工具 优点 缺点 适用场景
Apache HttpClient 功能全面、稳定 API 较繁琐 复杂请求场景(如自定义重试、拦截器)
OkHttp 高性能、API 简洁 高级功能需自定义 常规 HTTP 请求、Android 开发
WebClient 响应式、非阻塞、Spring 生态集成 学习曲线稍陡 Spring 5+ 项目、响应式编程
RestTemplate 简单易用 同步阻塞、已过时 旧 Spring 项目维护
HttpURLConnection 无需额外依赖 原始 API 难用 简单请求、避免引入新依赖
上次更新: 2025/06/19, 15:08:04
集成Redis及注意事项
认证授权与SpringSecurity_概念篇

← 集成Redis及注意事项 认证授权与SpringSecurity_概念篇→

最近更新
01
集成Redis及注意事项
06-08
02
Vue项目发布
06-04
03
MinIo跟对象存储服务
05-25
更多文章>
Theme by Vdoing | Copyright © 2023-2025 EffectTang
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式