Apache HttpClient 核心设计模式详解

在 Java 网络编程中,Apache HttpClient 是当之无愧的"明星组件"------它封装了复杂的 HTTP 协议细节,提供了稳定、高效的客户端能力,被广泛用于接口调用、爬虫开发、微服务通信等场景。

而它的强大,不仅在于功能的完备,更在于其底层架构中巧妙运用了多种设计模式,让代码具备极高的可扩展性、可维护性和复用性。

今天,我们就深入拆解 HttpClient 中最核心、最常用的 7 种设计模式,结合源码片段+实战实例,让你既能看懂设计思想,又能直接落地到项目中(建议收藏,面试、开发都能用)。

注:本文基于 HttpClient 4.5.x 版本(最主流稳定版本),所有实例均可直接复制运行,依赖如下(Maven):

java 复制代码
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version>
</dependency>
<!-- Apache HttpClient Mime 依赖 (用于 multipart 文件上传) -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.14</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.61</version>
</dependency>

一、建造者模式(Builder Pattern)------ 复杂对象的"优雅组装"

1. 模式核心原理

建造者模式的核心是:将复杂对象的构建过程与它的表示分离,通过一个"建造者"类,逐步设置对象的属性,最终构建出完整对象。适用于参数多、配置复杂的对象创建场景,避免构造方法冗余(比如一个类有 10 个参数,构造方法会极其繁琐)。

HttpClient 中,几乎所有复杂配置类都采用了建造者模式,最典型的就是 RequestConfig(请求配置)和 HttpClientBuilder(客户端构建器)。

2. HttpClient 中的应用实例

我们日常开发中,创建请求配置、自定义 HttpClient 实例,用的就是建造者模式,来看两个最常用的实战场景。

实例 1:构建 RequestConfig(请求超时、代理等配置)

RequestConfig 用于配置请求的超时时间、代理、重定向策略等,它的构造方法是私有(private)的,无法直接 new,必须通过内部建造者 RequestConfig.Builder 来构建。

java 复制代码
package com.example.apache.pattern;
import org.apache.http.client.config.RequestConfig;

public class BuilderPatternDemo1 {

    public static void main(String[] args) {
        // 1. 获取建造者实例(通过 custom() 方法)
        RequestConfig.Builder builder = RequestConfig.custom();
        // 2. 逐步设置配置参数(链式调用,优雅直观)
        RequestConfig requestConfig = builder
                .setConnectTimeout(5000) // 连接超时:5秒
                .setSocketTimeout(3000) // 读取超时:3秒
                .setConnectionRequestTimeout(2000) // 从连接池获取连接的超时:2秒
                .setProxy(null) // 不使用代理(可设置 HttpHost 实现代理)
                .setRedirectsEnabled(true) // 允许自动重定向
                .build(); // 3. 构建最终的 RequestConfig 对象
        // 打印配置信息(验证构建结果)
        System.out.println("连接超时:" + requestConfig.getConnectTimeout());
        System.out.println("读取超时:" + requestConfig.getSocketTimeout());
    }
}

实例 2:自定义 CloseableHttpClient 实例

默认的 HttpClients.createDefault() 只能满足基础需求,当我们需要配置连接池、重试策略、SSL 等高级功能时,就需要通过 HttpClientBuilder 来构建客户端。

java 复制代码
public class BuilderPatternDemo2 {
    public static void main(String[] args) {
        // 1. 获取 HttpClient 建造者
        HttpClientBuilder builder = HttpClients.custom();
        // 2. 配置高级参数(链式调用,按需添加)
        CloseableHttpClient httpClient = builder
                .setMaxConnTotal(100) // 连接池最大总连接数
                .setMaxConnPerRoute(10) // 每个路由(域名)的最大连接数
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectTimeout(5000)
                        .build()) // 设置默认请求配置
                .build(); // 3. 构建客户端实例
        // 后续使用 httpClient 发送请求(此处省略,下文有完整示例)
        System.out.println("自定义 HttpClient 构建完成:" + httpClient);
    }
}

3. 源码核心解析

以RequestConfig 为例,其源码核心结构如下(简化版),完美契合建造者模式的角色:

java 复制代码
public class RequestConfig implements Cloneable {
    // 成员变量(请求配置参数)
    private final int connectTimeout;
    private final int socketTimeout;
    // ... 其他参数
    
    // 私有构造方法(禁止外部直接创建)
    protected RequestConfig() {
       this(false, null, null, false, null, false, false, false, 0, false, null, null, 0, 0, 0, true, true);
    }
    
    // 内部建造者类(静态内部类,负责构建 RequestConfig)
    public static class Builder {
        private int connectTimeout;
        private int socketTimeout;
        
        // 无参构造(供外部获取)
        public Builder() {}
        
        // 链式设置方法(返回 Builder 自身,实现链式调用)
        public Builder setConnectTimeout(int connectTimeout) {
            this.connectTimeout = connectTimeout;
            return this;
        }
        
        public Builder setSocketTimeout(int socketTimeout) {
            this.socketTimeout = socketTimeout;
            return this;
        }
        
        // 构建最终对象(调用私有构造方法)
        public RequestConfig build() {
            return new RequestConfig(this);
        }
    }
    
    // 供外部获取建造者的静态方法
    public static RequestConfig.Builder custom() {
        return new Builder();
    }
    // ... getter 方法(无 setter,保证对象不可变)
}

关键点:RequestConfig 是不可变对象(无 setter 方法),一旦构建完成,参数无法修改,保证了线程安全;建造者通过链式调用,让配置过程清晰易懂,避免了多参数构造方法的冗余。

二、策略模式(Strategy Pattern)------ 灵活切换"行为算法"

1. 模式核心原理

策略模式的核心是:定义一系列算法(策略),将每个算法封装成独立的类,使得算法可以在运行时动态切换,且算法的变化不会影响使用算法的客户端。

HttpClient 中,很多"可配置行为"都采用了策略模式,比如连接重用策略、重定向策略、认证策略等------我们可以根据需求,替换不同的策略实现,而无需修改核心调用逻辑。
2. HttpClient 中的应用实例

最典型的是 ConnectionReuseStrategy(连接重用策略)和 RedirectStrategy(重定向策略),我们以"连接重用"和"重定向"为例,看实战用法。

实例 1:自定义连接重用策略

HTTP 连接重用(Keep-Alive)是提升性能的关键,HttpClient 提供了默认的连接重用策略 DefaultConnectionReuseStrategy,其逻辑是:根据请求/响应头(如 Connection: Keep-Alive)判断是否重用连接。我们也可以自定义策略,比如"强制不重用连接"。

java 复制代码
public class StrategyPatternDemo1 {

    public static void main(String[] args) {
        // 1. 自定义连接重用策略(实现 ConnectionReuseStrategy 接口)
        ConnectionReuseStrategy customReuseStrategy = new ConnectionReuseStrategy() {
            @Override
            public boolean keepAlive(HttpResponse response, HttpContext context) {
                // 自定义逻辑:强制不重用任何连接(无论响应头如何)
                return false;
            }
        };

        // 2. 构建 HttpClient 时,设置自定义策略(替换默认策略)
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionReuseStrategy(customReuseStrategy)
                .build();

        // 后续使用 httpClient 发送请求,所有连接都会在使用后关闭
        System.out.println("自定义连接重用策略已生效" + httpClient);
    }
}

补充:默认策略 DefaultConnectionReuseStrategy 的核心逻辑(简化):

请求头包含 Connection: Close → 不重用

响应头包含 Connection: Close → 不重用

响应头包含 Connection: Keep-Alive → 重用

无 Connection 头时,HTTP 版本 > 1.0 → 重用

实例 2:禁用重定向策略

HttpClient 默认会自动处理 301、302 等重定向响应(使用 DefaultRedirectStrategy),但在某些场景(如登录跳转、验证码获取),我们需要禁用重定向,手动处理跳转逻辑,此时只需替换重定向策略为NoRedirectStrategy。

java 复制代码
public class StrategyPatternDemo2 {
    public static void main(String[] args) throws Exception {
        // 1. 构建 HttpClient,设置"不重定向"策略
        CloseableHttpClient httpClient = HttpClients.custom()
                .setRedirectStrategy(new RedirectStrategy() {
                    @Override
                    public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) {
                        // 返回 false 表示HttpClient 不跟随重定向
                        return false;
                    }
                    @Override
                    public org.apache.http.client.methods.HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) {
                        // 不会被调用,因为 isRedirected 返回 false
                        return null;
                    }
                })
                .build();
        // 2. 发送一个会重定向的请求(比如 http://baidu.com 会重定向到 https://www.baidu.com)
        HttpGet httpGet = new HttpGet("http://baidu.com");
        // 3. 执行请求,获取响应(此时会返回 301 状态码,不会自动跳转)
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            int statusCode = response.getStatusLine().getStatusCode();
            System.out.println("响应状态码:" + statusCode); // 输出 301百度服务器返回的(永久重定向)
            System.out.println("重定向地址:" + response.getFirstHeader("Location").getValue()); // 输出 http://www.baidu.com/
            System.out.println("响应内容:" + EntityUtils.toString(response.getEntity()));
        }
    }
}

3. 源码核心解析

以 ConnectionReuseStrategy 为例,其核心结构是"接口+多个实现类",完美契合策略模式的角色:

策略接口:ConnectionReuseStrategy(定义核心方法 keepAlive)

具体策略:DefaultConnectionReuseStrategy(默认策略)、StrictConnectionReuseStrategy(严格策略)

策略使用方:HttpClientBuilder(通过 setConnectionReuseStrategy 注入策略)

源码简化版(接口定义):

java 复制代码
// 策略接口
public interface ConnectionReuseStrategy {
    // 核心方法:判断连接是否可以重用
    boolean keepAlive(HttpResponse response, HttpContext context);
}

// 具体策略(默认实现)
public class DefaultConnectionReuseStrategy implements ConnectionReuseStrategy {
    public static final DefaultConnectionReuseStrategy INSTANCE = new DefaultConnectionReuseStrategy();
    
    @Override
    public boolean keepAlive(HttpResponse response, HttpContext context) {
        // 具体的重用判断逻辑
        // ...
    }
}

三、外观模式(Facade Pattern)------ 复杂系统的"简化入口"

1. 模式核心原理

外观模式的核心是:为复杂的子系统提供一个统一的入口(外观类),隐藏子系统的复杂性,让客户端只需调用外观类的方法,就能完成复杂的操作,无需关心子系统的内部细节。

HttpClient 中,HttpClients 工具类就是典型的外观类------它封装了 HttpClientBuilder、RequestConfig 等子系统的复杂逻辑,提供了简洁的静态方法,让我们无需关注构建细节,就能快速创建客户端实例。

2. HttpClient 中的应用实例

我们日常最常用的 HttpClients.createDefault(),就是外观模式的典型应用,来看对比:

实例:外观模式 vs 直接使用子系统

实例代码:

java 复制代码
public class FacadePatternDemo {
  
    public static void main(String[] args) {
        // 方式1:使用外观类 HttpClients(简洁,推荐)
        CloseableHttpClient httpClient1 = HttpClients.createDefault();
        System.out.println("通过外观类创建客户端:" + httpClient1);
        // 方式2:直接使用子系统(HttpClientBuilder,复杂)
        CloseableHttpClient httpClient2 = HttpClientBuilder.create()
                .build();
        System.out.println("直接使用子系统创建客户端:" + httpClient2);
        // 两种方式创建的客户端,功能完全一致(外观类内部就是调用了 HttpClientBuilder)
    }
}

除了 createDefault(),HttpClients 还提供了多个外观方法,覆盖不同场景:

HttpClients.createSystem():基于系统属性创建客户端

HttpClients.custom():获取建造者,用于自定义配置(外观类与建造者模式结合)

HttpClients.createMinimal():创建最小化客户端(仅包含必要配置)
3. 源码核心解析

HttpClients 类的源码简化版(核心就是调用子系统的方法,隐藏细节):

java 复制代码
public class HttpClients {
    // 私有构造方法,禁止实例化(纯工具类)
    private HttpClients() {}
    
    // 外观方法:创建默认客户端
    public static CloseableHttpClient createDefault() {
        // 内部调用 HttpClientBuilder(子系统),隐藏构建细节
        return HttpClientBuilder.create().build();
    }
    
    // 外观方法:获取建造者,用于自定义配置
    public static HttpClientBuilder custom() {
        return HttpClientBuilder.create();
    }
    
    // 外观方法:基于系统属性创建客户端
    public static CloseableHttpClient createSystem() {
        return HttpClientBuilder.create()
                .useSystemProperties()
                .build();
    }
}

关键点:外观模式不改变子系统的功能,只是提供了一个"简化入口"。对于简单场景,我们用外观类快速开发;对于复杂场景,我们仍可以直接操作子系统(如 HttpClientBuilder)进行自定义配置,兼顾了简洁性和灵活性。

四、装饰器模式(Decorator Pattern)------ 动态扩展对象功能

1. 模式核心原理

装饰器模式的核心是:动态地给一个对象添加额外的功能,而无需修改原对象的代码,且装饰器可以嵌套使用,实现功能的叠加。它比继承更灵活,能避免类的爆炸式增长。

HttpClient 中,HttpEntity(请求/响应体)的实现大量使用了装饰器模式------通过装饰器,我们可以给原始的 Entity 增加缓存、压缩、编码等功能,而无需修改原始 Entity 的代码。

2. HttpClient 中的应用实例

最常用的是 BufferedHttpEntity(缓存装饰器)和 GzipCompressingEntity(压缩装饰器),我们以"缓存响应体"为例,看实战用法。

实例:使用 BufferedHttpEntity 缓存响应体

原始的 HttpEntity(如 BasicHttpEntity)是流式的,只能读取一次,一旦读取完毕,就无法再次获取内容。而 BufferedHttpEntity 作为装饰器,会将原始 Entity 的内容缓存到内存中,支持多次读取。

java 复制代码
public class DecoratorPatternDemo {

    public static void main(String[] args) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            // 1. 获取原始响应体(流式,只能读取一次)
            HttpEntity originalEntity = response.getEntity();

            // 2. 使用 BufferedHttpEntity 装饰原始 Entity(添加缓存功能)
            BufferedHttpEntity bufferedEntity = new BufferedHttpEntity(originalEntity);

            // 3. 多次读取响应体(装饰器生效)
            String content1 = EntityUtils.toString(bufferedEntity, "UTF-8");
            String content2 = EntityUtils.toString(bufferedEntity, "UTF-8");

            System.out.println("第一次读取内容:" + content1);
            System.out.println("第二次读取内容:" + content2);
            System.out.println("两次读取内容是否一致:" + content1.equals(content2)); // 输出 true
        }
    }
}

补充:其他常用装饰器:

GzipCompressingEntity:对请求体进行 GZIP 压缩,减少传输体积

GzipDecompressingEntity:对响应体进行 GZIP 解压,自动处理压缩的响应

UrlEncodedFormEntity:对表单数据进行 URL 编码,用于 POST 请求提交表单
3. 源码核心解析

以 BufferedHttpEntity 为例,其核心结构是"继承+组合",完美契合装饰器模式的角色:

抽象组件:HttpEntity(定义了响应体的核心方法,如 getContent())

具体组件:BasicHttpEntity(原始响应体,流式实现)

装饰器:BufferedHttpEntity(继承 HttpEntity,组合了原始 Entity,添加缓存功能)

源码简化版(核心逻辑):

java 复制代码
// 抽象组件(HttpEntity)
public interface HttpEntity {
    InputStream getContent() throws IOException;
    // ... 其他方法
}

// 具体组件(原始响应体)
public class BasicHttpEntity implements HttpEntity {
    private InputStream content;
    
    @Override
    public InputStream getContent() throws IOException {
        return content; // 流式,读取一次后关闭
    }
}

// 装饰器(添加缓存功能)
public class BufferedHttpEntity implements HttpEntity {
    private final HttpEntity wrappedEntity; // 组合原始组件
    private byte[] buffer; // 缓存数据
    
    // 构造方法:传入原始组件
    public BufferedHttpEntity(HttpEntity entity) throws IOException {
        this.wrappedEntity = entity;
        // 将原始组件的内容读取到缓存中
        this.buffer = EntityUtils.toByteArray(entity);
    }
    
    // 重写 getContent() 方法,返回缓存的数据流
    @Override
    public InputStream getContent() throws IOException {
        return new ByteArrayInputStream(buffer); // 每次返回新的流,支持多次读取
    }
}

五、工厂模式(Factory Pattern)------ 标准化对象创建

1. 模式核心原理

工厂模式的核心是:定义一个工厂类,负责创建其他类的实例,隐藏对象创建的细节,实现"创建与使用分离"。HttpClient 中,工厂模式主要用于创建 HTTP 请求对象(如 HttpGet、HttpPost)和客户端实例。
2. HttpClient 中的应用实例

最典型的是 RequestBuilder(请求工厂),它可以根据请求方法(GET、POST、PUT 等),动态创建对应的请求对象(HttpGet、HttpPost 等),无需我们手动 new 不同的请求类。

实例:使用 RequestBuilder 创建不同类型的请求

java 复制代码
public class FactoryPatternDemo {
    public static void main(String[] args) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 1. 使用 RequestBuilder 创建 GET 请求(工厂方法)
        HttpUriRequest getRequest = RequestBuilder.get()
                .setUri("https://jsonplaceholder.typicode.com/posts/1")
                .addHeader("Content-Type", "application/json")
                .build();
        // 2. 使用 RequestBuilder 创建 POST 请求(工厂方法)
        HttpUriRequest postRequest = RequestBuilder.post()
                .setUri("https://jsonplaceholder.typicode.com/posts")
                .addHeader("Content-Type", "application/json")
                .setEntity(new StringEntity("{\"title\":\"test\",\"body\":\"test body\",\"userId\":1}"))
                .build();
        // 执行 GET 请求
        try (var response = httpClient.execute(getRequest)) {
            System.out.println("GET 响应:" + EntityUtils.toString(response.getEntity()));
        }
        // 执行 POST 请求
        try (var response = httpClient.execute(postRequest)) {
            System.out.println("POST 响应:" + EntityUtils.toString(response.getEntity()));
        }
    }
}

补充:RequestBuilder 支持的请求方法:get()、post()、put()、delete()、head() 等,覆盖所有 HTTP 标准方法,返回对应的请求对象(如 get() 返回 HttpGet,post() 返回 HttpPost)。

六、模板方法模式(Template Method Pattern)------ 固定流程,灵活扩展

1. 模式核心原理

模板方法模式的核心是:定义一个操作的算法骨架(模板方法),将算法的某些步骤延迟到子类中实现,使得子类可以在不改变算法骨架的前提下,重新定义算法的某些步骤。

HttpClient 中,HttpClient.execute() 方法的底层实现,就采用了模板方法模式------它定义了发送请求的完整流程(创建连接、发送请求、接收响应、释放资源),将"具体的请求发送逻辑"延迟到子类中实现。
2. HttpClient 中的应用实例

我们无需手动实现模板方法的骨架,只需调用 execute() 方法,HttpClient 会自动执行完整流程,我们可以通过自定义 ResponseHandler 来扩展"响应处理"步骤。

实例:使用 ResponseHandler 扩展响应处理逻辑

java 复制代码
public class TemplateMethodDemo {
    public static void main(String[] args) throws Exception {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");
        // 自定义 ResponseHandler(扩展响应处理步骤)
        ResponseHandler<String> responseHandler = response -> {
            int statusCode = response.getStatusLine().getStatusCode();
            // 步骤1:自定义状态码判断
            if (statusCode >= 200 && statusCode < 300) {
                // 步骤2:自定义响应体处理
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            } else {
                // 步骤3:自定义异常处理
                throw new RuntimeException("请求失败,状态码:" + statusCode);
            }
        };
        // 执行请求(模板方法:execute() 定义了完整流程,responseHandler 扩展了响应处理)
        String responseContent = httpClient.execute(httpGet, responseHandler);
        System.out.println("请求响应:" + responseContent);
    }
}

3. 源码核心解析

HttpClient 的 execute() 方法(模板方法)底层逻辑简化如下:

java 复制代码
// 模板方法(定义算法骨架)
public <T> T execute(HttpUriRequest request, ResponseHandler<T> responseHandler) throws IOException {
    // 步骤1:创建连接(固定逻辑)
    HttpContext context = createHttpContext();
    // 步骤2:发送请求,获取响应(固定逻辑)
    CloseableHttpResponse response = execute(request, context);
    try {
        // 步骤3:处理响应(延迟到 ResponseHandler 实现,灵活扩展)
        return responseHandler.handleResponse(response);
    } finally {
        // 步骤4:释放资源(固定逻辑)
        response.close();
    }
}

关键点:模板方法execute() 固定了"创建连接→发送请求→处理响应→释放资源"的流程,其中"处理响应"这一步骤通过 ResponseHandler 延迟到客户端实现,既保证了流程的一致性,又提供了灵活的扩展能力。

七、过滤器(拦截器)------ 请求/响应的"拦截与增强"(结合装饰器+责任链模式)

你提到的"过滤器",在 Apache HttpClient 中对应的是 HttpRequestInterceptor(请求拦截器)和 HttpResponseInterceptor(响应拦截器),它本质是装饰器模式与责任链模式的结合------通过拦截请求/响应,在其发送/接收的前后添加额外逻辑(如添加请求头、打印日志、加密解密等),且多个拦截器可按顺序执行,形成责任链。

拦截器是 HttpClient 中非常实用的功能,日常开发中几乎离不开,比如统一添加 Token、打印请求日志、处理跨域头、对请求体加密等,都可以通过拦截器实现,无需侵入业务代码。

1. 拦截器的核心设计逻辑(装饰器+责任链)

HttpClient 的拦截器机制,完美融合了两种设计模式的优势:

装饰器模式:拦截器不修改原始的请求/响应对象,而是通过"包装"的方式,动态添加额外功能(如给请求添加头信息),符合"开闭原则"(对扩展开放,对修改关闭)。

责任链模式:我们可以添加多个拦截器,HttpClient 会按添加顺序依次执行它们(请求拦截器先添加先执行,响应拦截器先添加后执行),每个拦截器负责自己的逻辑,互不干扰,可灵活组合。

核心接口:

HttpRequestInterceptor:请求拦截器,在请求发送之前执行,用于处理请求(如添加请求头、签名、加密)。

HttpResponseInterceptor:响应拦截器,在接收响应之后、交给业务逻辑之前执行,用于处理响应(如解密、日志打印、统一异常处理)。

2. 实战实例:常用拦截器场景(可直接复制使用)

下面结合 3 个高频实战场景,演示拦截器的使用,覆盖日志打印、统一请求头、响应解密(简化版),帮你快速落地。

实例 1:请求/响应日志拦截器(调试必备)

开发中经常需要查看请求的详细信息(请求地址、请求头、请求体)和响应信息(状态码、响应头、响应体),通过拦截器可以统一打印,无需在每个请求中重复写日志代码。

java 复制代码
public class InterceptorDemo1 {
    public static void main(String[] args) throws Exception {
        // 1. 创建请求拦截器(打印请求信息)
        HttpRequestInterceptor requestInterceptor = (request, context) -> {
            System.out.println("=== 请求拦截器执行 ==");
            System.out.println("请求方法:" + request.getRequestLine().getMethod());
            System.out.println("请求地址:" + request.getRequestLine().getUri());
            // 安全地打印请求头
            if (request.getAllHeaders().length > 0) {
                System.out.println("请求头示例:" + request.getAllHeaders()[0]);
            } else {
                System.out.println("请求头:无");
            }
        };
        // 2. 创建响应拦截器(只打印响应状态和头信息,不读取响应体)
        HttpResponseInterceptor responseInterceptor = (response, context) -> {
            System.out.println("\n=== 响应拦截器执行 ==");
            System.out.println("响应状态码:" + response.getStatusLine().getStatusCode());
            System.out.println("响应原因:" + response.getStatusLine().getReasonPhrase());
            // 打印前几个响应头
            if (response.getAllHeaders().length > 0) {
                System.out.println("响应头示例:");
                for (int i = 0; i < Math.min(3, response.getAllHeaders().length); i++) {
                    System.out.println("  " + response.getAllHeaders()[i]);
                }
            }
            // 注意:不在拦截器中读取响应体,避免流消耗问题
        };
        // 3. 构建 HttpClient,添加拦截器(可添加多个,按顺序执行)
        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .addInterceptorFirst(requestInterceptor) // 添加请求拦截器
                .addInterceptorLast(responseInterceptor) // 添加响应拦截器
                .build();
        // 4. 发送请求,拦截器自动执行
        HttpGet httpGet = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            // 在主逻辑中读取响应体(HttpClient 会自动处理 gzip 解压)
            String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
            System.out.println("\n=== 主逻辑读取响应体 ==");
            System.out.println(responseBody);
        }
    }
}

运行结果说明:请求发送前,请求拦截器打印请求信息;响应接收后,响应拦截器打印响应信息,调试时无需修改业务代码,直接添加拦截器即可。

实例 2:统一添加请求头(如 Token、Content-Type)

项目中所有请求都需要添加统一的请求头(如 Token、Content-Type、User-Agent),通过拦截器统一处理,避免在每个请求中重复添加,减少冗余代码。

java 复制代码
public class InterceptorDemo2 {
    public static void main(String[] args) throws Exception {
        // 统一请求头拦截器
        HttpRequestInterceptor headerInterceptor = (request, context) -> {
            // 统一添加 Token(实际项目中从配置/上下文获取)
            request.addHeader("Token", "abc123456789");
            // 统一设置 Content-Type
            request.addHeader("Content-Type", "application/json;charset=UTF-8");
            // 统一设置 User-Agent
            request.addHeader("User-Agent", "Apache-HttpClient/4.5.14");
        };
        // 构建 HttpClient,添加拦截器
        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .addInterceptorFirst(headerInterceptor)
                .build();
        // 发送 POST 请求,无需手动添加请求头
        HttpPost httpPost = new HttpPost("https://jsonplaceholder.typicode.com/posts");
        httpPost.setEntity(new StringEntity("{\"title\":\"test\",\"body\":\"test body\",\"userId\":1}"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            System.out.println("请求成功,响应:" + EntityUtils.toString(response.getEntity()));
        }
    }
}

关键点:拦截器中添加的请求头,会作用于所有通过该 HttpClient 发送的请求,后续新增请求无需再关注请求头配置,降低维护成本。

实例 3:响应解密拦截器(简化版)

如果接口返回的响应体是加密的(如 AES 加密),可以通过响应拦截器统一解密,业务逻辑中直接获取明文,无需重复编写解密代码。

java 复制代码
public class InterceptorDemo3 {
    public static void main(String[] args) throws Exception {
        // 响应解密拦截器(简化版,实际项目中替换为真实加密算法)
        HttpResponseInterceptor decryptInterceptor = (response, context) -> {
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                try {
                    // 1. 获取响应体(此时 HttpClient 已自动处理 gzip 解压)
                    String encryptedContent = EntityUtils.toString(entity, "UTF-8");
                    // 2. 解密(此处用简单替换模拟加密,实际用 AES/RSA 等)
                    String decryptedContent = encryptedContent.replace("encrypt_", "");
                    // 3. 替换响应体为明文(装饰器模式核心:包装原始响应体)
                    response.setEntity(new StringEntity(decryptedContent, "UTF-8"));
                    System.out.println("=== 解密拦截器执行 ==");
                    System.out.println("原始内容长度:" + encryptedContent.length() + " 字符");
                } catch (IOException e) {
                    System.err.println("解密失败:" + e.getMessage());
                }
            }
        };
        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .addInterceptorLast(decryptInterceptor) // 使用 addInterceptorLast,在解压缩之后执行
                .build();
        // 模拟请求加密接口(响应体为 encrypt_xxx)
        HttpGet httpGet = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            // 业务逻辑直接获取明文响应体
            String decryptedContent = EntityUtils.toString(response.getEntity(), "UTF-8");
            System.out.println("\n=== 业务逻辑获取响应 ==");
            System.out.println("解密后的响应体:");
            System.out.println(decryptedContent);
        }
    }
}

3. 源码核心解析(拦截器的执行机制)

HttpClient 中,拦截器的执行由 HttpClientBuilder 管理,底层通过"责任链"模式依次执行所有拦截器,核心逻辑简化如下:

// HttpClientBuilder 中管理拦截器的核心代码(简化)

java 复制代码
public class HttpClientBuilder {
    // 存储请求拦截器的列表(责任链)
    private LinkedList<HttpRequestInterceptor> requestFirst;
    private LinkedList<HttpRequestInterceptor> requestLast;
    // 存储响应拦截器的列表(责任链)
    private LinkedList<HttpResponseInterceptor> responseFirst;
    private LinkedList<HttpResponseInterceptor> responseLast;
    // 添加请求拦截器,加到链表头部
    public final HttpClientBuilder addInterceptorFirst(final HttpResponseInterceptor itcp) {
        responseFirst.addFirst(itcp);
        return this;
    }
    // 添加请求拦截器,加到链表尾部
    public final HttpClientBuilder addInterceptorLast(final HttpResponseInterceptor itcp) {
        responseLast.addLast(itcp);
        return this;
    }
    // 添加响应拦截器加到链表头部
    public final HttpClientBuilder addInterceptorFirst(final HttpResponseInterceptor itcp) {
        responseFirst.addFirst(itcp);
        return this;
    }
    // 构建客户端时,将拦截器注入到执行器中
    private CloseableHttpClient build() {
        // 初始化执行器,将拦截器传入 简化代码
        ClientExecChain execChain = createMainExec(requestInterceptors, responseInterceptors);
        return new InternalHttpClient(execChain, ...);
    }
}

执行流程(责任链顺序):

1、请求发送前:按"添加顺序"执行所有请求拦截器(addInterceptorFirst 添加的先执行)。

2、请求发送后、接收响应前:执行核心请求逻辑(与服务器交互)。

3、接收响应后:按"添加顺序的逆序"执行所有响应拦截器(addInterceptorFirst 添加的后执行)。

关键点:拦截器的核心是"无侵入式增强"------不修改原始请求/响应的核心逻辑,仅通过拦截的方式添加额外功能,且多个拦截器可灵活组合,符合设计模式的"开闭原则"。

总结:HttpClient 设计模式的核心价值

Apache HttpClient 之所以能成为 Java 网络编程的"标杆",不仅在于其功能的完备,更在于其底层设计模式的合理运用------将复杂的 HTTP 协议细节,通过设计模式封装成简洁、易用、可扩展的 API。

本文总结的 7 种核心设计模式(含拦截器对应的装饰器+责任链),覆盖了 HttpClient 的大部分核心场景,整理成表格方便记忆:

相关推荐
bmseven2 小时前
23种设计模式 - 抽象工厂模式(Abstract Factory)
设计模式·抽象工厂模式
想你依然心痛3 小时前
大数据时代时序数据库选型指南:Apache IoTDB 如何成为工业物联网的“数据底座“
大数据·apache·时序数据库
木斯佳18 小时前
HarmonyOS 6 三方SDK对接:从半接模式看Share Kit原理——系统分享的运行机制与设计理念
设计模式·harmonyos·架构设计·分享·半接模式
yydonk19 小时前
像 Agent 一样思考:从 Claude Code 架构演进看 AI Agent 工具设计
设计模式
Jackson_Li21 小时前
大多数人对 Claude Code Skills 的理解,在第一步就错了
人工智能·设计模式
似水明俊德1 天前
13-C#.Net-设计模式六大原则-学习笔记
笔记·学习·设计模式·c#·.net
wangchunting1 天前
Java设计模式
java·单例模式·设计模式
孟陬2 天前
国外技术周刊 #3:“最差程序员”带动高效团队、不写代码的创业导师如何毁掉创新…
前端·后端·设计模式
程序 代码狂人2 天前
Apache旗下产品有哪些
apache