OkHttp3.X 工具类封装:链式调用,支持HTTPS、重试、文件上传【内含常用设计模式设计示例】

OkHttp3.X 工具类封装:链式调用,支持HTTPS、重试、文件上传

基于OkHttp3.X封装,提供链式调用API,简化GET/POST请求,支持HTTPS、自动重试、文件上传等功能,提升开发效率。


在 Android 和 Java 开发中,OkHttp 是最常用的网络请求库之一。为了简化日常开发中的重复配置,提升开发效率,本文将分享一个线程安全、支持默认与自定义配置、链式调用的 OkHttp 工具类,并详细解析其核心功能与实现细节。

一、工具类核心优势

  1. 双重配置模式
    • 默认配置:预定义通用参数(超时时间、连接池、重试策略),适用于大多数场景。
    • 自定义配置:基于默认配置扩展,支持添加拦截器、修改超时参数、配置 HTTPS 证书等。
  2. 线程安全
    • 使用双重检查锁定实现单例,确保多线程环境下实例唯一。
  3. 链式请求构建
    • 支持 GET/POST 请求,灵活拼接 URL 参数、请求头、请求体(FormData、JSON、二进制等)。
  4. HTTPS 安全适配
    • 支持调试模式(跳过证书验证)、自定义证书(自签名证书)、系统默认证书三种模式。
  5. 统一异常处理
    • 自动关闭响应体,封装非 2xx 状态码为 HttpException,简化错误处理逻辑。

二、快速开始

1. 引入依赖

<dependency>
    <groupId>com.squareup.okhttp3groupId>
    <artifactId>okhttpartifactId>
    <version>4.12.0version>
dependency>

2. 工具类核心功能

2.1 GET请求
// 带URL参数和Headers
String result = OkHttp_Util.get("https://api.example.com")
    .addUrlParam("page", "1")
    .addHeader("Authorization", "Bearer token123")
    .execute_Pro();

// 批量添加参数
Map<String, String> params = new HashMap<>();
params.put("key1", "value1");
params.put("key2", "value2");
String result = OkHttp_Util.get("https://api.example.com")
    .addUrlParam(params)
    .execute_Pro();
2.2 POST请求
// 提交JSON
String json = "{\"name\":\"Dolphin\",\"age\":25}";
String result = OkHttp_Util.post("https://api.example.com")
    .jsonBody(json)
    .execute_Pro();

// FormData表单
Map<String, String> formData = new HashMap<>();
formData.put("username", "admin");
formData.put("password", "123456");
String result = OkHttp_Util.post("https://api.example.com")
    .formData(formData)
    .execute_Pro();

// 文件上传
File file = new File("avatar.jpg");
MediaType mediaType = MediaType.parse("image/jpeg");
String result = OkHttp_Util.post("https://api.upload.com")
    .formData("file", file, mediaType)
    .execute_Pro();
2.3 自定义配置
// 创建自定义Client(添加拦截器+长超时)
OkHttpClient.Builder builder = OkHttp_Util.customBuilder()
    .addInterceptor(new OkHttp_Util.LoggingInterceptor())
    .connectTimeout(30, TimeUnit.SECONDS);

OkHttp_Util.buildCustomInstance(builder);
OkHttpClient customClient = OkHttp_Util.getCustomInstance();

// 使用自定义Client发起请求
String result = OkHttp_Util.get("https://api.example.com", customClient)
    .addUrlParam("debug", "true")
    .execute_Pro();
2.4 异步请求
OkHttp_Util.get("https://api.example.com")
    .enqueue(new Callback() {
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String body = OkHttp_Util.parseResponse(response);
            // 处理响应
        }

        @Override
        public void onFailure(Call call, IOException e) {
            // 处理失败
        }
    });

三、核心设计解析

3.1 单例模式管理

// 默认配置单例
private static volatile OkHttpClient defaultInstance;

// 自定义配置单例
private static volatile OkHttpClient customInstance;

public static OkHttpClient getDefaultInstance() {
    if (defaultInstance == null) {
        synchronized (OkHttp_Util.class) {
            if (defaultInstance == null) {
                defaultInstance = defaultBuilder().build();
            }
        }
    }
    return defaultInstance;
}

3.2 HTTPS安全配置

支持三种模式:

  • 调试模式:跳过证书验证(仅测试环境)
  • 自定义证书:指定PEM证书
  • 系统默认:使用系统CA证书
private static OkHttpClient.Builder configSSL(OkHttpClient.Builder builder, OkHttpConfig config) {
    // 调试模式:信任所有证书
    if (config.debugMode) {
        builder.sslSocketFactory(createInsecureSocketFactory(), new TrustAllManager());
    }
    // 自定义证书
    else if (config.trustedCertificates != null) {
        X509TrustManager trustManager = createCustomTrustManager(config.trustedCertificates);
        builder.sslSocketFactory(trustManager.getSocketFactory(), trustManager);
    }
    // 系统默认证书
    else {
        X509TrustManager trustManager = getSystemTrustManager();
        builder.sslSocketFactory(trustManager.getSocketFactory(), trustManager);
    }
    return builder;
}

3.3 重试拦截器

private static class RetryInterceptor implements Interceptor {
    private final int maxRetries;

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        IOException exception = null;

        for (int i = 0; i <= maxRetries; i++) {
            try {
                Response response = chain.proceed(request);
                if (response.isSuccessful()) return response;
            } catch (IOException e) {
                exception = e;
            }
        }
        throw exception != null ? exception : new IOException("请求失败");
    }
}

四、注意事项

  1. HTTPS安全:生产环境务必使用系统证书或自定义证书
  2. 资源释放:同步请求自动关闭响应体,异步需手动关闭
  3. 超时配置:根据业务需求调整默认超时时间
  4. 线程安全:OkHttpClient实例线程安全,建议复用
  5. 异常处理:使用execute_Pro()自动处理非200响应

五、完整代码

package com.dolphin.util;

import okhttp3.*;

import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * OkHttp 工具类(线程安全)
 * 

* 提供两种 OkHttpClient 实例管理模式: *

    *
  • 【默认配置】预定义通用网络参数,适用于大多数场景: *
      *
    • 连接/读取/写入超时:10 秒
    • *
    • 允许重定向和失败重试(最大 3 次)
    • *
    • 连接池:5 个空闲连接,保持 10 分钟
    • *
    • 系统默认证书信任策略(非调试模式)
    • *
    *
  • *
  • 【自定义配置】基于默认配置扩展,支持灵活定制: *
      *
    • 添加拦截器(日志、签名、认证等)
    • *
    • 修改超时参数、连接池配置
    • *
    • 配置 HTTPS 证书信任策略(调试模式/自定义证书/系统默认)
    • *
    *
  • *
*

* 使用示例: *

 * // 1. 使用默认配置发起 GET 请求
 * String resultDefault = OkHttp_Util.get("https://api.example.com")
 *     .addUrlParam("page", "1")
 *     .addHeader("User-Agent", "OkHttp-Util/1.0")
 *     .execute_Pro(); // 自动处理响应和异常
 *
 * // 2. 自定义配置(添加日志拦截器+长连接超时)
 * OkHttpClient.Builder customBuilder = OkHttp_Util.customBuilder()
 *     .addInterceptor(new OkHttp_Util.LoggingInterceptor()) // 添加自定义拦截器
 *     .connectTimeout(60, TimeUnit.SECONDS); // 延长连接超时时间
 * OkHttp_Util.buildCustomInstance(customBuilder); // 初始化自定义单例
 * OkHttpClient customClient = OkHttp_Util.getCustomInstance(); // 获取到 一个 单例,并且是自定义配置的 OkHttpClient 实例
 * String resultCustom = OkHttp_Util.get("https://api.example.com", customClient)
 *     .addUrlParam("page", "1")
 *     .addHeader("User-Agent", "OkHttp-Util/1.0")
 *     .execute_Pro(); // 自动处理响应和异常
 *
 * // 3. 上传文件(multipart/form-data)
 * File file = new File("example.pdf");
 * String uploadResult = OkHttp_Util.post("https://api.upload.com")
 *     .addHeader("Authorization", "Bearer YOUR_TOKEN")
 *     .formData("file", file, MediaType.parse("application/pdf")) // 上传文件
 *     .execute_Pro();
 *
 * // 4. 处理异步请求
 * OkHttp_Util.get("https://api.async.com")
 *     .enqueue(new Callback() {
 *         @Override
 *         public void onResponse(Call call, Response response) throws IOException {
 *             String body = OkHttp_Util.parseResponse(response);
 *             // 处理成功响应
 *         }
 *         @Override
 *         public void onFailure(Call call, IOException e) {
 *             // 处理失败(网络异常)
 *         }
 *     });
 * 
* * @author DolphinHome * @date 2025/04/30 */
public class OkHttp_Util { /* // 使用 Log4j 记录日志 private static final Logger log = LoggerFactory.getLogger(OkHttp_Util.class); */ // ------------------------------ 单例实例 ------------------------------ /** * 默认配置的 OkHttpClient 单例(线程安全,双重检查锁定实现) */ private static volatile OkHttpClient defaultInstance; /** * 自定义配置的 OkHttpClient 单例(线程安全,按需初始化) */ private static volatile OkHttpClient customInstance; // ------------------------------ 构造方法 ------------------------------ /** * 私有构造方法,禁止类实例化 *

* 通过断言错误防止反射创建实例,强化工具类设计 *

* * @throws AssertionError 始终抛出,明确禁止实例化 */
private OkHttp_Util() { throw new AssertionError("不可实例化工具类"); } // ------------------------------ 配置类 ------------------------------ /** * OkHttp 配置参数容器 *

* 包含网络请求核心配置,所有参数均有默认值: *

    *
  • 超时:连接/读取/写入默认 10 秒
  • *
  • 重定向:默认允许
  • *
  • 重试:默认允许,最大 3 次
  • *
  • 连接池:5 个空闲连接,保持 10 分钟
  • *
  • HTTPS:默认使用系统证书,调试模式可跳过验证
  • *
* *

* 自定义证书信任(生产环境): *

     * // 1. 加载 PEM 证书文件
     * X509Certificate cert = loadCertificateFromFile("trusted.crt");
     * OkHttpConfig config = new OkHttpConfig();
     * config.setTrustedCertificates(new X509Certificate[]{cert}); // 设置自定义证书
     *
     * // 2. 通过自定义构建器应用配置(需修改 defaultBuilder 逻辑,此处仅示例)
     * OkHttpClient.Builder builder = OkHttp_Util.customBuilder();
     * configSSL(builder, config); // 内部方法,实际通过 OkHttpConfig 传递
     * 
*

* 调试模式(测试环境): *

     * OkHttpConfig debugConfig = new OkHttpConfig();
     * debugConfig.setDebugMode(true); // 跳过所有证书验证(危险!仅测试用)
     * 
*/
public static class OkHttpConfig { // 超时配置(单位:秒) private int connectTimeout; // 连接超时:建立 TCP 连接的最大等待时间 private int readTimeout; // 读取超时:等待服务器响应数据的最大时间 private int writeTimeout; // 写入超时:发送请求体到服务器的最大时间 // 重定向策略 private boolean followRedirects; // 是否允许自动跟随重定向(默认允许) // 重试策略 private boolean retryOnConnectionFailure; // 连接失败时是否重试(默认允许) private int maxRetryCount; // 最大重试次数(默认 3 次) // 连接池配置 private int maxIdleConnections = 5; // 最大空闲连接数(默认 5 个) private long keepAliveDuration = 10; // 连接存活时间(默认 10 分钟) private TimeUnit keepAliveUnit = TimeUnit.MINUTES; // 存活时间单位 // HTTPS 安全配置 private boolean debugMode; // 调试模式(跳过证书验证,默认关闭) private X509Certificate[] trustedCertificates; // 自定义信任证书(PEM 格式,默认 null) /** * 无参构造:使用全默认配置 */ public OkHttpConfig() { this( 10, 10, 10, true, true, 3, 5, 10, TimeUnit.MINUTES, false, null ); } public OkHttpConfig( int connectTimeout, int readTimeout, int writeTimeout, boolean followRedirects, boolean retryOnConnectionFailure, int maxRetryCount, int maxIdleConnections, int keepAliveDuration, TimeUnit keepAliveUnit, boolean debugMode, X509Certificate[] trustedCertificates ) { this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; this.writeTimeout = writeTimeout; this.followRedirects = followRedirects; this.retryOnConnectionFailure = retryOnConnectionFailure; this.maxRetryCount = maxRetryCount; this.maxIdleConnections = maxIdleConnections; this.keepAliveDuration = keepAliveDuration; this.keepAliveUnit = keepAliveUnit; this.debugMode = debugMode; this.trustedCertificates = trustedCertificates; } // region get/set 方法 public int getConnectTimeout() { return connectTimeout; } public void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; } public int getReadTimeout() { return readTimeout; } public void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; } public int getWriteTimeout() { return writeTimeout; } public void setWriteTimeout(int writeTimeout) { this.writeTimeout = writeTimeout; } public boolean isFollowRedirects() { return followRedirects; } public void setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; } public boolean isRetryOnConnectionFailure() { return retryOnConnectionFailure; } public void setRetryOnConnectionFailure(boolean retryOnConnectionFailure) { this.retryOnConnectionFailure = retryOnConnectionFailure; } public int getMaxRetryCount() { return maxRetryCount; } public void setMaxRetryCount(int maxRetryCount) { this.maxRetryCount = maxRetryCount; } public int getMaxIdleConnections() { return maxIdleConnections; } public void setMaxIdleConnections(int maxIdleConnections) { this.maxIdleConnections = maxIdleConnections; } public long getKeepAliveDuration() { return keepAliveDuration; } public void setKeepAliveDuration(long keepAliveDuration) { this.keepAliveDuration = keepAliveDuration; } public TimeUnit getKeepAliveUnit() { return keepAliveUnit; } public void setKeepAliveUnit(TimeUnit keepAliveUnit) { this.keepAliveUnit = keepAliveUnit; } public boolean isDebugMode() { return debugMode; } public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; } public X509Certificate[] getTrustedCertificates() { return trustedCertificates; } public void setTrustedCertificates(X509Certificate[] trustedCertificates) { this.trustedCertificates = trustedCertificates; } // endregion } // ------------------------------ 自定义配置单例 ------------------------------ /** * 获取自定义配置的 OkHttpClient 单例 *

* 必须先通过 {@link #buildCustomInstance(OkHttpClient.Builder)} 初始化 *

*

* 使用示例: *

*
     * OkHttpClient.Builder customBuilder = OkHttp_Util.customBuilder()
     *     .addInterceptor(new OkHttp_Util.LoggingInterceptor()) // 添加自定义拦截器
     *     .connectTimeout(60, TimeUnit.SECONDS); // 延长连接超时时间
     * OkHttp_Util.buildCustomInstance(customBuilder); // 初始化自定义单例
     * OkHttpClient customClient = OkHttp_Util.getCustomInstance(); // 获取到 一个 单例,并且是自定义配置的 OkHttpClient 实例
     * String resultCustom = OkHttp_Util.get("https://api.example.com", customClient)
     *     .addUrlParam("page", "1")
     *     .addHeader("User-Agent", "OkHttp-Util/1.0")
     *     .execute_Pro(); // 自动处理响应和异常
     * 
* * @return 自定义配置的 OkHttpClient 实例(线程安全单例) * @throws IllegalStateException 未初始化自定义配置时抛出 */
public static OkHttpClient getCustomInstance() { if (customInstance == null) { throw new IllegalStateException("自定义 OkHttpClient 未初始化,请先调用 buildCustomInstance()"); } return customInstance; } /** * 初始化自定义配置单例 *

* 基于默认配置构建器扩展,支持添加拦截器、修改超时等 *

* 线程安全:采用双重检查锁定,确保单例唯一性 *

*

* 使用示例: *

*
     * OkHttpClient.Builder customBuilder = OkHttp_Util.customBuilder()
     *     .addInterceptor(new OkHttp_Util.LoggingInterceptor()) // 添加自定义拦截器
     *     .connectTimeout(60, TimeUnit.SECONDS); // 延长连接超时时间
     * OkHttp_Util.buildCustomInstance(customBuilder); // 初始化自定义单例
     * OkHttpClient customClient = OkHttp_Util.getCustomInstance(); // 获取到 一个 单例,并且是自定义配置的 OkHttpClient 实例
     * String resultCustom = OkHttp_Util.get("https://api.example.com", customClient)
     *     .addUrlParam("page", "1")
     *     .addHeader("User-Agent", "OkHttp-Util/1.0")
     *     .execute_Pro(); // 自动处理响应和异常
     * 
* * @param builder OkHttpClient 构建器(不可为 null) * @throws NullPointerException builder 为 null 时抛出 */
public static void buildCustomInstance(OkHttpClient.Builder builder) { if (builder == null) throw new NullPointerException("builder 不能为 null"); if (customInstance == null) { synchronized (OkHttp_Util.class) { if (customInstance == null) { customInstance = builder.build(); } } } } /** * 获取可扩展的 OkHttpClient 构建器(基于默认配置) *

* 每次调用返回新的构建器实例,不影响现有单例: * - 包含默认的超时时间、连接池和重试策略 * - 支持添加自定义拦截器(如签名、认证、日志等) * - 可修改 HTTPS 证书信任策略 *

*

* 使用示例: *

*
     * // 自定义配置 OkHttpClient 单例
     * OkHttpClient.Builder customBuilder = OkHttp_Util.customBuilder()
     *     .addInterceptor(new OkHttp_Util.LoggingInterceptor()) // 添加自定义拦截器
     *     .connectTimeout(60, TimeUnit.SECONDS); // 延长连接超时时间
     * OkHttp_Util.buildCustomInstance(customBuilder); // 初始化自定义单例
     * OkHttpClient customClient = OkHttp_Util.getCustomInstance(); // 获取到 一个 单例,并且是自定义配置的 OkHttpClient 实例【注意:此客户端是单例的,调用后会将其设置为单例实例】
     * String resultCustom = OkHttp_Util.get("https://api.example.com", customClient)
     *     .addUrlParam("page", "1")
     *     .addHeader("User-Agent", "OkHttp-Util/1.0")
     *     .execute_Pro(); // 自动处理响应和异常
     *
     *
     * // 另一个 OkHttpClient 实例(非单例)
     * OkHttpClient customClient = OkHttp_Util.customBuilder()
     *      .addInterceptor(new XXXXXInterceptor())
     *      .connectTimeout(60, TimeUnit.SECONDS)
     *      .build(); // 获取到了一个自定义配置的 OkHttpClient 实例【注意:此客户端不是单例的,每次调用都会创建一个新的客户端】
     * String resultCustom = OkHttp_Util.get("https://api.example.com", customClient)
     *     .addUrlParam("page", "1")
     *     .addHeader("User-Agent", "OkHttp-Util/1.0")
     *     .execute_Pro(); // 自动处理响应和异常
     * 
* * @return 预配置默认参数的 OkHttpClient.Builder 实例 */
public static OkHttpClient.Builder customBuilder() { // 通过newBuilder()方法克隆默认配置,保证原实例不受影响 return getDefaultInstance().newBuilder(); } // ------------------------------ 默认配置单例 ------------------------------ /** * 获取默认配置的 OkHttpClient 单例 *

* 首次调用时初始化,采用双重检查锁定保证线程安全 *

* 默认配置详情: *

    *
  • 超时:10 秒(连接/读取/写入)
  • *
  • 连接池:5 个空闲连接,保持 10 分钟
  • *
  • 重试策略:3 次失败重试(通过 {@link RetryInterceptor} 实现)
  • *
  • HTTPS:使用系统默认证书信任策略(非调试模式)
  • *
*

*

* 使用示例: *

*
     * OkHttpClient client = OkHttp_Util.getDefaultInstance(); // 获取到 一个 单例,并且默认配置过的 OkHttpClient 实例
     * Request request = new Request.Builder()
     *     .url("https://api.default.com")
     *     .get()
     *     .build();
     * try (Response response = client.newCall(request).execute()) {
     *     String body = OkHttp_Util.parseResponse(response);
     * }
     * 
* * @return 预配置的 OkHttpClient 单例(线程安全) */
public static OkHttpClient getDefaultInstance() { // 第一次检查:避免每次访问都进行同步 if (defaultInstance == null) { synchronized (OkHttp_Util.class) { // 第二次检查:防止重复创建 if (defaultInstance == null) { defaultInstance = defaultBuilder().build(); // 使用默认的自定义配置 } } } return defaultInstance; } /** * 创建默认配置的 OkHttpClient.Builder *

* 包含以下核心配置: *

    *
  1. 基础超时参数(10 秒)
  2. *
  3. 连接池配置(5 个空闲连接,10 分钟存活)
  4. *
  5. 重试拦截器(最大 3 次重试)
  6. *
  7. HTTPS 证书信任策略(根据 {@link OkHttpConfig} 动态配置)
  8. *
*

* * @return 预配置默认参数的构建器实例 */
private static OkHttpClient.Builder defaultBuilder() { // 自定义配置 OkHttpConfig config = new OkHttpConfig(); OkHttpClient.Builder builder = new OkHttpClient.Builder() // 设置连接超时(建立TCP连接的最大等待时间) .connectTimeout(config.connectTimeout, TimeUnit.SECONDS) // 设置读取超时(等待服务器返回数据的最大时间) .readTimeout(config.readTimeout, TimeUnit.SECONDS) // 设置写入超时(发送请求体到服务器的最大时间) .writeTimeout(config.writeTimeout, TimeUnit.SECONDS) // 设置是否允许重定向 .followRedirects(config.followRedirects) // 设置是否允许失败重试 .retryOnConnectionFailure(config.retryOnConnectionFailure) // 配置连接池(复用HTTP/HTTP2连接,减少延迟) .connectionPool(new ConnectionPool( config.maxIdleConnections, // 最大空闲连接数 config.keepAliveDuration, // 保持连接时间 config.keepAliveUnit)) // 保持连接时间单位 // TODO: 通过 Interceptor 实现 最大重试次数 .addInterceptor(new RetryInterceptor(config.maxRetryCount)); // 设置信任证书(PEM格式)【测试环境中 debug=true 时,不设置信任证书】 configSSL(builder, config); return builder; } // ------------------------------ HTTPS 安全配置 ------------------------------ /** * 配置 HTTPS 证书信任策略 *

* 支持三种模式: *

    *
  • 调试模式({@code debugMode=true}): *
      *
    • 跳过所有证书验证(存在严重安全风险,仅限测试环境!)
    • *
    • 警告:可能导致中间人攻击,绝不能用于生产环境
    • *
    *
  • *
  • 自定义证书({@code trustedCertificates 非空}): *
      *
    • 仅信任指定的 PEM 格式证书
    • *
    • 适用于双向认证或自签名证书场景
    • *
    *
  • *
  • 系统默认(默认模式): *
      *
    • 使用设备/系统预装的 CA 证书
    • *
    • 适用于生产环境的标准 HTTPS 通信
    • *
    *
  • *
*

* * @param builder OkHttpClient 构建器实例 * @param config 配置参数(包含 HTTPS 相关配置) * @return 应用证书策略后的构建器实例 * @throws RuntimeException 证书配置失败时抛出(包装底层异常) */
private static OkHttpClient.Builder configSSL(OkHttpClient.Builder builder, OkHttpConfig config) { try { SSLContext sslContext; X509TrustManager trustManager; if (config.debugMode) { // 调试模式:禁用证书验证(风险提示:仅用于测试环境!) sslContext = SSLContext.getInstance("TLSv1.3"); trustManager = new TrustAllManager(); sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom()); builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager); } else if (config.trustedCertificates != null && config.trustedCertificates.length > 0) { // 自定义证书模式:使用指定的 PEM 证书 trustManager = createCustomTrustManager(config.trustedCertificates); sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(null, new TrustManager[]{trustManager}, null); builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager); } else { // 生产环境默认:使用系统信任的证书 trustManager = getSystemTrustManager(); sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(null, new TrustManager[]{trustManager}, null); builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager); } // 允许所有主机名验证(需根据实际需求调整,此处为演示简化) builder.hostnameVerifier((hostname, session) -> true); } catch (Exception e) { throw new RuntimeException("HTTPS 证书配置失败", e); } return builder; } // region 安全组件 /** * 调试模式专用:信任所有证书的管理器 *

* 重写证书验证方法,跳过所有客户端和服务器证书检查 * 警告:此实现存在严重安全漏洞,仅限测试环境使用! *

*/
@Deprecated private static class TrustAllManager implements 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]; // 返回空列表表示接受所有颁发者 } } /** * 调试模式专用:创建忽略证书验证的 SSLSocketFactory *

* 警告:此模式会绕过所有证书验证,存在中间人攻击风险,仅限测试环境使用! *

* * @return 不安全的 SSLSocketFactory 实例 * @throws Exception SSL 上下文初始化失败时抛出 */
private static SSLSocketFactory createInsecureSocketFactory() throws Exception { SSLContext context = SSLContext.getInstance("TLSv1.3"); context.init(null, new TrustManager[]{new TrustAllManager()}, new SecureRandom()); return context.getSocketFactory(); } /** * 获取系统默认的证书信任管理器 *

* 使用设备或系统预装的证书颁发机构 (CA) 列表 *

* * @return 系统默认的 X509TrustManager 实例 * @throws Exception 信任管理器初始化失败时抛出 */
private static X509TrustManager getSystemTrustManager() throws Exception { TrustManagerFactory factory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); factory.init((KeyStore) null); return (X509TrustManager) factory.getTrustManagers()[0]; } /** * 创建自定义证书信任管理器(PEM 格式) *

* 将指定的证书添加到信任列表,用于验证服务器证书 *

* * @param certificates PEM 格式的 X509 证书数组 * @return 自定义的 X509TrustManager 实例 * @throws Exception 密钥库或信任管理器初始化失败时抛出 */
private static X509TrustManager createCustomTrustManager(X509Certificate[] certificates) throws Exception { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); for (int i = 0; i < certificates.length; i++) { keyStore.setCertificateEntry("cert-" + i, certificates[i]); } TrustManagerFactory factory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); factory.init(keyStore); return (X509TrustManager) factory.getTrustManagers()[0]; } // endregion // region 拦截器 private static class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long startNs = System.nanoTime(); // 记录请求详情 // log.debug("Request => {} {}\nHeaders: {}", request.method(), request.url(), request.headers()); Response response = chain.proceed(request); long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); // 记录响应指标 // log.info("Response <= {} {} ({}ms)\nCode: {}", request.method(), request.url(), costMs, response.code()); return response; } } /** * 失败重试拦截器(基于默认配置的最大重试次数) *

* 实现逻辑: *

    *
  • 对每个请求尝试 {@code maxRetries + 1} 次(包括首次)
  • *
  • 仅处理 {@link IOException}(网络层异常)
  • *
  • 成功响应({@code response.isSuccessful()})立即返回
  • *
*

*/
private static class RetryInterceptor implements Interceptor { private final int maxRetries; // 最大重试次数(不包含首次请求) /** * @param maxRetries 最大重试次数(建议 0-5,默认 3) */ public RetryInterceptor(int maxRetries) { this.maxRetries = maxRetries; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); IOException exception = null; for (int i = 0; i <= maxRetries; i++) { // 包括首次请求,总尝试次数 maxRetries + 1 try { Response response = chain.proceed(request); if (response.isSuccessful()) return response; // 非成功响应不重试(如 4xx/5xx 错误,由上层处理) return response; } catch (IOException e) { exception = e; // log.warn("请求失败(尝试 {}/{}): {}", i + 1, maxRetries + 1, e.getMessage()); System.out.println(String.format("请求失败(尝试 %d/%d): %s", i + 1, maxRetries + 1, e.getMessage())); } } throw exception != null ? exception : new IOException("网络请求无响应"); } } // endregion // region 统一响应处理 /** * 解析响应并处理异常 *

* 自动关闭响应体,封装非成功状态码为 {@link HttpException} *

* * @param response OkHttp 响应实例(不可为 null) * @return 响应正文(UTF-8 字符串) * @throws IOException 网络异常或响应体为空 * @throws HttpException 非 2xx 状态码(包含状态码和消息) */
public static String parseResponse(Response response) throws IOException { try { if (!response.isSuccessful()) { throw new HttpException(response.code(), "HTTP错误: " + response.code() + " - " + response.message()); } ResponseBody body = response.body(); if (body == null) throw new HttpException(500, "响应体为空"); return body.string(); } finally { response.close(); } } /** * 自定义 HTTP 异常(包含状态码) */ public static class HttpException extends IOException { private final int statusCode; // HTTP 状态码(如 404, 500) public HttpException(int statusCode, String message) { super(message); this.statusCode = statusCode; } public int getStatusCode() { return statusCode; } } // endregion // region 链式调用 /** * 链式请求构建器(支持 GET/POST 请求) *

* POST 提交 JSON 数据示例: *

     * String jsonBody = "{\"name\":\"Dolphin\",\"age\":18}";
     * String result = OkHttp_Util.post("https://api.post.com")
     *     .addHeader("Content-Type", "application/json")
     *     .jsonBody(jsonBody) // 设置 JSON 请求体
     *     .execute_Pro(); // 自动处理 200 以外状态码
     * 
*

* GET 请求带查询参数示例: *

     * Map params = new HashMap<>();
     * params.put("key1", "value1");
     * params.put("key2", "value2");
     * String result = OkHttp_Util.get("https://api.get.com")
     *     .addUrlParam(params) // 批量添加查询参数
     *     .execute_Pro();
     * 
*/
public static class RequestChainBuilder { // region MediaType 常量 // 定义 JSON 数据的媒体类型,字符集为 UTF-8 private static final MediaType JSON_TYPE = MediaType.get("application/json; charset=utf-8"); // 定义表单数据的媒体类型,字符集为 UTF-8 private static final MediaType FORM_URLENCODED_TYPE = MediaType.get("application/x-www-form-urlencoded; charset=utf-8"); // 定义纯文本数据的媒体类型,字符集为 UTF-8 private static final MediaType TEXT_PLAIN_TYPE = MediaType.get("text/plain; charset=utf-8"); // 定义 XML 数据的媒体类型,字符集为 UTF-8 private static final MediaType XML_TYPE = MediaType.get("application/xml; charset=utf-8"); // endregion private final OkHttpClient client; // 使用的 OkHttpClient 实例(默认或自定义) private final HttpUrl.Builder urlBuilder; // URL 构建器(包含基础 URL 和查询参数) private final Request.Builder requestBuilder; // 请求构建器(包含头信息和方法) private RequestBody requestBody; // 请求体(仅 POST/PUT 等方法需要) /** * 私有构造方法(内部使用,外部通过工厂方法创建) * * @param url 基础 URL(不可为 null,需包含协议如 http://) * @param client OkHttpClient 实例(默认或自定义) * @throws NullPointerException 当 url 或 client 为 null 时抛出 */ private RequestChainBuilder(String url, OkHttpClient client) { // 确保 client 不为 null,若为 null 则抛出异常 this.client = Objects.requireNonNull(client, "client 不能为 null"); // 解析 URL 并创建 URL 构建器,若 URL 格式错误则抛出异常 this.urlBuilder = Objects.requireNonNull(HttpUrl.parse(url), "url 格式错误").newBuilder(); // 创建请求构建器 this.requestBuilder = new Request.Builder(); } /** * 添加单个 URL 查询参数(链式调用)。 * * @param key 参数名,不可为 null * @param value 参数值,可为 null(将被编码为空字符串) * @return 当前构建器实例,支持链式调用 * @throws IllegalArgumentException 当 key 为 null 时抛出 */ public RequestChainBuilder addUrlParam(String key, String value) { // 确保参数名不为空,若为空则抛出异常 if (key == null) throw new IllegalArgumentException("参数名不能为空"); // 向 URL 构建器中添加查询参数,若值为 null 则编码为空字符串 urlBuilder.addQueryParameter(key, value != null ? value : ""); // 返回当前构建器实例,以支持链式调用 return this; } /** * 添加单个请求头字段(链式调用)。 * * @param key 头字段名,不可为 null * @param value 头字段值,可为 null(OkHttp 会自动处理 null 值) * @return 当前构建器实例,支持链式调用 * @throws IllegalArgumentException 当 key 为 null 时抛出 */ public RequestChainBuilder addHeader(String key, String value) { if (key == null) throw new IllegalArgumentException("头字段名不能为空"); requestBuilder.addHeader(key, value); return this; } /** * 批量添加 URL 参数 * * @param params 包含要添加的 URL 参数的 Map * @return 当前构建器实例 */ public RequestChainBuilder addUrlParam(Map<String, String> params) { // 若参数不为空,则遍历参数并调用 addUrlParam 方法添加每个参数 if (params != null) { for (Map.Entry<String, String> entry : params.entrySet()) { addUrlParam(entry.getKey(), entry.getValue()); } } // 返回当前构建器实例,以支持链式调用 return this; } /** * 批量添加请求头 * * @param headers 包含要添加的请求头的 Map * @return 当前构建器实例 */ public RequestChainBuilder addHeaders(Map<String, String> headers) { // 若头信息不为空,则遍历头信息并调用 addHeader 方法添加每个头信息 if (headers != null) { for (Map.Entry<String, String> entry : headers.entrySet()) { addHeader(entry.getKey(), entry.getValue()); } } // 返回当前构建器实例,以支持链式调用 return this; } /****************** GET请求封装 ********************/ /** * 设置请求方法为 GET * GET 请求不需要请求体,调用后可继续添加查询参数或请求头 * * @return 当前构建器实例 */ public RequestChainBuilder get() { // 向请求构建器中设置请求方法为 GET requestBuilder.get(); // 返回当前构建器实例,以支持链式调用 return this; } /****************** POST请求封装 ********************/ /** * 设置请求方法为 POST * 调用后需通过 {@link #formData(Map)}、{@link #jsonBody(String)} 等方法设置请求体 * * @return 当前构建器实例 */ public RequestChainBuilder post() { // 默认设置空请求体 this.requestBody = RequestBody.create(null, new byte[0]); // 返回当前构建器实例,以支持链式调用 return this; } // ------------------------------ Body 参数配置 ------------------------------ /** * 设置 FormData 表单参数(multipart/form-data) * 自动构建 {@link MultipartBody},媒体类型为 {@code multipart/form-data} * * @param params 表单参数(键值对) * @return 当前实例 */ public RequestChainBuilder formData(Map<String, String> params) { // 参数校验 if (params == null) throw new IllegalArgumentException("参数 params 不能为空"); // 创建 MultipartBody 构建器,并设置媒体类型为 multipart/form-data MultipartBody.Builder bodyBuilder = new MultipartBody.Builder() .setType(MultipartBody.FORM); // 遍历参数并添加到 MultipartBody 构建器中 params.forEach(bodyBuilder::addFormDataPart); // 构建请求体 this.requestBody = bodyBuilder.build(); // 返回当前构建器实例,以支持链式调用 return this; } /** * 设置 multipart/form-data 表单参数(含文件上传) *

* 示例:上传单个文件 *

         * File avatar = new File("avatar.png");
         * MediaType imageType = MediaType.parse("image/png");
         * String uploadResult = OkHttp_Util.post("https://api.upload.com")
         *     .formData("file", avatar, imageType) // 文件参数
         *     .addFormDataPart("username", "dolphin") // 普通表单参数(需配合 MultipartBody.Builder)
         *     .execute_Pro();
         * 
* * @param key 表单字段名 * @param file 上传文件 * @param mediaType 文件类型(如 MediaType.parse("image/png")) * @return 当前构建器实例 */
public RequestChainBuilder formData(String key, File file, MediaType mediaType) { // 参数校验 if (key == null) throw new IllegalArgumentException("参数 key 不能为空"); if (file == null) throw new IllegalArgumentException("参数 file 不能为空"); if (mediaType == null) throw new IllegalArgumentException("参数 mediaType 不能为空"); // 创建 MultipartBody 构建器,并设置媒体类型为 multipart/form-data MultipartBody.Builder builder = new MultipartBody.Builder() .setType(MultipartBody.FORM) // 添加文件部分,设置 Content-Disposition 头信息 .addPart( Headers.of("Content-Disposition", "form-data; name=\"" + key + "\"; filename=\"" + file.getName() + "\""), RequestBody.create(mediaType, file) ); // 构建请求体 this.requestBody = builder.build(); // 返回当前构建器实例,以支持链式调用 return this; } /** * 设置 x-www-form-urlencoded 表单参数 * 自动构建 {@link FormBody},媒体类型为 {@code application/x-www-form-urlencoded} * * @param params 表单参数(键值对) * @return 当前实例 */ public RequestChainBuilder formUrlEncoded(Map<String, String> params) { // 参数校验 if (params == null) throw new IllegalArgumentException("参数 params 不能为空"); // 创建 FormBody 构建器 FormBody.Builder bodyBuilder = new FormBody.Builder(); // 遍历参数并添加到 FormBody 构建器中 params.forEach(bodyBuilder::add); // 构建请求体 this.requestBody = bodyBuilder.build(); // 返回当前构建器实例,以支持链式调用 return this; } /** * 设置 Raw JSON 数据 * 自动设置媒体类型为 {@code application/json; charset=utf-8} * * @param json JSON 字符串 * @return 当前实例 */ public RequestChainBuilder jsonBody(String json) { // 参数校验 if (json == null) throw new IllegalArgumentException("参数 json 不能为空"); // 创建 JSON 格式的请求体 this.requestBody = RequestBody.create(json, JSON_TYPE); // 返回当前构建器实例,以支持链式调用 return this; } /** * 设置 Raw 文本数据 * 自动设置媒体类型为 {@code text/plain; charset=utf-8} * * @param text 文本内容 * @return 当前实例 */ public RequestChainBuilder rawText(String text) { // 参数校验 if (text == null) throw new IllegalArgumentException("参数 text 不能为空"); // 创建纯文本格式的请求体 this.requestBody = RequestBody.create(text, TEXT_PLAIN_TYPE); // 返回当前构建器实例,以支持链式调用 return this; } /** * 设置 Raw XML 数据 * 自动设置媒体类型为 {@code application/xml; charset=utf-8} * * @param xml XML 字符串 * @return 当前实例 */ public RequestChainBuilder rawXml(String xml) { // 参数校验 if (xml == null) throw new IllegalArgumentException("参数 xml 不能为空"); // 创建 XML 格式的请求体 this.requestBody = RequestBody.create(xml, XML_TYPE); // 返回当前构建器实例,以支持链式调用 return this; } /** * 设置二进制数据 * 媒体类型为 {@code application/octet-stream} * * @param data 二进制字节数组 * @return 当前实例 */ public RequestChainBuilder binaryBody(byte[] data) { // 参数校验 if (data == null) throw new IllegalArgumentException("参数 data 不能为空"); // 创建二进制格式的请求体 this.requestBody = RequestBody.create(data, MediaType.get("application/octet-stream")); // 返回当前构建器实例,以支持链式调用 return this; } /** * 执行同步请求并返回原始响应对象。 *

* 适用于需要手动处理响应状态码和响应体的场景,需调用者手动关闭响应体。 * * @return OkHttp 的 {@link Response} 对象 * @throws IOException 网络异常、请求超时或服务器错误 */ public Response execute() throws IOException { // 构建最终URL HttpUrl httpUrl = urlBuilder.build(); // POST请求设置请求体 if (requestBody != null) { // 若请求体不为空,则设置请求方法为 POST 并设置请求体 requestBuilder.method("POST", requestBody); } // 设置URL并构建请求 Request request = requestBuilder.url(httpUrl).build(); // 执行请求并返回响应 return client.newCall(request).execute(); } /** * 执行同步请求并返回处理后的响应字符串(自动处理非成功状态码)。 *

* 功能包括: *

    *
  • 自动关闭响应体(通过 try-with-resources)
  • *
  • 检查响应状态码,非 2xx 状态码抛出 {@link OkHttp_Util.HttpException}
  • *
  • 将响应体转换为 UTF-8 编码的字符串
  • *
* 自动关闭响应体,处理非成功状态码({@code response.isSuccessful()}) *

* * @return 响应正文(UTF-8 字符串) * @throws IOException 网络异常、响应体解析失败或 I/O 错误 * @throws OkHttp_Util.HttpException 当响应状态码非 2xx 时抛出,包含状态码和错误消息 */
public String execute_Pro() throws IOException { // 构建最终URL HttpUrl httpUrl = urlBuilder.build(); // POST请求设置请求体 if (requestBody != null) { // 若请求体不为空,则设置请求方法为 POST 并设置请求体 requestBuilder.method("POST", requestBody); } // 设置URL并构建请求 Request request = requestBuilder.url(httpUrl).build(); // 执行请求并自动关闭响应体 try (Response response = client.newCall(request).execute()) { // 解析响应并返回响应正文 return OkHttp_Util.parseResponse(response); } } /** * 执行异步请求,通过回调处理响应结果。 *

* 支持处理成功响应({@link Callback#onResponse(Call, Response)})和失败情况({@link Callback#onFailure(Call, IOException)})。 *

* **注意**:异步请求不会自动关闭响应体,需在 {@link Callback#onResponse(Call, Response)} 中手动关闭。 * * @param callback 异步回调接口,不可为 null * @throws NullPointerException 当 callback 为 null 时抛出 */ public void enqueue(Callback callback) { try { // 检查 OkHttpClient 实例是否为 null if (client == null) { // 若为 null,则调用回调的 onFailure 方法并传入异常信息 callback.onFailure(null, new IOException("OkHttpClient 实例为 null")); return; } // 构建请求 Request request = requestBuilder.url(urlBuilder.build()).build(); // 检查请求是否为 null if (request == null) { // 若为 null,则调用回调的 onFailure 方法并传入异常信息 callback.onFailure(null, new IOException("请求构建失败,Request 为 null")); return; } // 执行异步请求 client.newCall(request).enqueue(callback); } catch (IllegalStateException e) { // 记录 URL 构建失败的日志 // log.error("URL构建失败", e); System.err.println("URL构建失败: " + e.getMessage()); // 调用回调的 onFailure 方法并传入异常信息 callback.onFailure(null, new IOException("URL构建失败")); } catch (Exception e) { // 记录异步请求失败的日志 // log.error("异步请求失败", e); System.err.println("异步请求失败: " + e.getMessage()); // 调用回调的 onFailure 方法并传入异常信息 callback.onFailure(null, new IOException("异步请求失败")); } } } /** * 重载工厂方法:创建一个 GET 请求的链式构建器 * * @param url 请求的 URL * @return 链式构建器实例 */ public static RequestChainBuilder get(String url) { // 创建一个 RequestChainBuilder 实例并设置请求方法为 GET return new RequestChainBuilder(url, OkHttp_Util.getDefaultInstance()).get(); } /** * 重载工厂方法:创建一个 POST 请求的链式构建器 * * @param url 请求的 URL * @return 链式构建器实例 */ public static RequestChainBuilder post(String url) { // 创建一个 RequestChainBuilder 实例并设置请求方法为 POST return new RequestChainBuilder(url, OkHttp_Util.getDefaultInstance()).post(); } /** * 重载工厂方法:支持传入自定义 OkHttpClient 实例,创建一个使用自定义 OkHttpClient 实例的 GET 请求的链式构建器 * * @param url 请求的 URL * @param client 自定义的 OkHttpClient 实例 * @return 链式构建器实例 */ public static RequestChainBuilder get(String url, OkHttpClient client) { // 创建一个 RequestChainBuilder 实例并设置请求方法为 GET,使用自定义的 OkHttpClient 实例 return new RequestChainBuilder(url, client).get(); } /** * 重载工厂方法:支持传入自定义 OkHttpClient 实例,创建一个使用自定义 OkHttpClient 实例的 POST 请求的链式构建器 * * @param url 请求的 URL * @param client 自定义的 OkHttpClient 实例 * @return 链式构建器实例 */ public static RequestChainBuilder post(String url, OkHttpClient client) { // 创建一个 RequestChainBuilder 实例并设置请求方法为 POST,使用自定义的 OkHttpClient 实例 return new RequestChainBuilder(url, client).post(); } // endregion public static void main(String[] args) throws IOException { // 使用默认配置 OkHttpClient defaultClient = OkHttp_Util.getDefaultInstance(); // ------------------------------------------------------------------------------- String responseDefault = OkHttp_Util.get("https://api.example.com") .addUrlParam("key", "value") .execute_Pro(); // ------------------------------------------------------------------------------- // 创建自定义配置 OkHttpClient.Builder customBuilder = OkHttp_Util.customBuilder() .addInterceptor(new LoggingInterceptor()) .connectTimeout(30, TimeUnit.SECONDS); // 提交并实例化自定义配置(单例) OkHttp_Util.buildCustomInstance(customBuilder); // 获取自定义配置实例 OkHttpClient customClient = OkHttp_Util.getCustomInstance(); String responseCustom = OkHttp_Util.get("https://api.example.com", customClient) .addUrlParam("key", "value") .execute_Pro(); } }


六、总结

本工具类通过封装OkHttp的复杂配置,提供以下优势:

  • 开发效率提升:链式API简化网络请求编写
  • 可维护性强:统一配置入口,修改全局参数方便
  • 安全性增强:标准化HTTPS证书管理
  • 健壮性保障:内置重试机制和异常处理

适合中大型项目作为基础网络组件使用,建议根据实际业务需求调整超时时间和重试策略。

你可能感兴趣的:(生产环境_场景抽象,代码规范,https,设计模式,python)