OkHttp3.X 工具类封装:链式调用,支持HTTPS、重试、文件上传【内含常用设计模式设计示例】
OkHttp3.X 工具类封装:链式调用,支持HTTPS、重试、文件上传
基于OkHttp3.X封装,提供链式调用API,简化GET/POST请求,支持HTTPS、自动重试、文件上传等功能,提升开发效率。
在 Android 和 Java 开发中,OkHttp 是最常用的网络请求库之一。为了简化日常开发中的重复配置,提升开发效率,本文将分享一个线程安全、支持默认与自定义配置、链式调用的 OkHttp 工具类,并详细解析其核心功能与实现细节。
一、工具类核心优势
- 双重配置模式
- 默认配置:预定义通用参数(超时时间、连接池、重试策略),适用于大多数场景。
- 自定义配置:基于默认配置扩展,支持添加拦截器、修改超时参数、配置 HTTPS 证书等。
- 线程安全
- 使用双重检查锁定实现单例,确保多线程环境下实例唯一。
- 链式请求构建
- 支持 GET/POST 请求,灵活拼接 URL 参数、请求头、请求体(FormData、JSON、二进制等)。
- HTTPS 安全适配
- 支持调试模式(跳过证书验证)、自定义证书(自签名证书)、系统默认证书三种模式。
- 统一异常处理
- 自动关闭响应体,封装非 2xx 状态码为
HttpException
,简化错误处理逻辑。
- 自动关闭响应体,封装非 2xx 状态码为
二、快速开始
1. 引入依赖
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.12.0</version>
</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() {@Overridepublic void onResponse(Call call, Response response) throws IOException {String body = OkHttp_Util.parseResponse(response);// 处理响应}@Overridepublic 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;@Overridepublic 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("请求失败");}
}
四、注意事项
- HTTPS安全:生产环境务必使用系统证书或自定义证书
- 资源释放:同步请求自动关闭响应体,异步需手动关闭
- 超时配置:根据业务需求调整默认超时时间
- 线程安全:OkHttpClient实例线程安全,建议复用
- 异常处理:使用
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 工具类(线程安全)* <p>* 提供两种 OkHttpClient 实例管理模式:* <ul>* <li>【默认配置】预定义通用网络参数,适用于大多数场景:* <ul>* <li>连接/读取/写入超时:10 秒</li>* <li>允许重定向和失败重试(最大 3 次)</li>* <li>连接池:5 个空闲连接,保持 10 分钟</li>* <li>系统默认证书信任策略(非调试模式)</li>* </ul>* </li>* <li>【自定义配置】基于默认配置扩展,支持灵活定制:* <ul>* <li>添加拦截器(日志、签名、认证等)</li>* <li>修改超时参数、连接池配置</li>* <li>配置 HTTPS 证书信任策略(调试模式/自定义证书/系统默认)</li>* </ul>* </li>* </ul>* <p>* 使用示例:* <pre>* // 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) {* // 处理失败(网络异常)* }* });* </pre>** @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;// ------------------------------ 构造方法 ------------------------------/*** 私有构造方法,禁止类实例化* <p>* 通过断言错误防止反射创建实例,强化工具类设计* </p>** @throws AssertionError 始终抛出,明确禁止实例化*/private OkHttp_Util() {throw new AssertionError("不可实例化工具类");}// ------------------------------ 配置类 ------------------------------/*** OkHttp 配置参数容器* <p>* 包含网络请求核心配置,所有参数均有默认值:* <ul>* <li>超时:连接/读取/写入默认 10 秒</li>* <li>重定向:默认允许</li>* <li>重试:默认允许,最大 3 次</li>* <li>连接池:5 个空闲连接,保持 10 分钟</li>* <li>HTTPS:默认使用系统证书,调试模式可跳过验证</li>* </ul>** <p>* 自定义证书信任(生产环境):* <pre>* // 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 传递* </pre>* <p>* 调试模式(测试环境):* <pre>* OkHttpConfig debugConfig = new OkHttpConfig();* debugConfig.setDebugMode(true); // 跳过所有证书验证(危险!仅测试用)* </pre>*/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 单例* <p>* 必须先通过 {@link #buildCustomInstance(OkHttpClient.Builder)} 初始化* </p>* <p>* 使用示例:* </p>* <pre>* 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(); // 自动处理响应和异常* </pre>** @return 自定义配置的 OkHttpClient 实例(线程安全单例)* @throws IllegalStateException 未初始化自定义配置时抛出*/public static OkHttpClient getCustomInstance() {if (customInstance == null) {throw new IllegalStateException("自定义 OkHttpClient 未初始化,请先调用 buildCustomInstance()");}return customInstance;}/*** 初始化自定义配置单例* <p>* 基于默认配置构建器扩展,支持添加拦截器、修改超时等* <p>* <b>线程安全</b>:采用双重检查锁定,确保单例唯一性* </p>* <p>* 使用示例:* </p>* <pre>* 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(); // 自动处理响应和异常* </pre>** @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 构建器(基于默认配置)* <p>* 每次调用返回新的构建器实例,不影响现有单例:* - 包含默认的超时时间、连接池和重试策略* - 支持添加自定义拦截器(如签名、认证、日志等)* - 可修改 HTTPS 证书信任策略* </p>* <p>* 使用示例:* </p>* <pre>* // 自定义配置 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(); // 自动处理响应和异常* </pre>** @return 预配置默认参数的 OkHttpClient.Builder 实例*/public static OkHttpClient.Builder customBuilder() {// 通过newBuilder()方法克隆默认配置,保证原实例不受影响return getDefaultInstance().newBuilder();}// ------------------------------ 默认配置单例 ------------------------------/*** 获取默认配置的 OkHttpClient 单例* <p>* 首次调用时初始化,采用双重检查锁定保证线程安全* <p>* 默认配置详情:* <ul>* <li>超时:10 秒(连接/读取/写入)</li>* <li>连接池:5 个空闲连接,保持 10 分钟</li>* <li>重试策略:3 次失败重试(通过 {@link RetryInterceptor} 实现)</li>* <li>HTTPS:使用系统默认证书信任策略(非调试模式)</li>* </ul>* </p>* <p>* 使用示例:* </p>* <pre>* 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);* }* </pre>** @return 预配置的 OkHttpClient 单例(线程安全)*/public static OkHttpClient getDefaultInstance() {// 第一次检查:避免每次访问都进行同步if (defaultInstance == null) {synchronized (OkHttp_Util.class) {// 第二次检查:防止重复创建if (defaultInstance == null) {defaultInstance = defaultBuilder().build(); // 使用默认的自定义配置}}}return defaultInstance;}/*** 创建默认配置的 OkHttpClient.Builder* <p>* 包含以下核心配置:* <ol>* <li>基础超时参数(10 秒)</li>* <li>连接池配置(5 个空闲连接,10 分钟存活)</li>* <li>重试拦截器(最大 3 次重试)</li>* <li>HTTPS 证书信任策略(根据 {@link OkHttpConfig} 动态配置)</li>* </ol>* </p>** @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 证书信任策略* <p>* 支持三种模式:* <ul>* <li><b>调试模式</b>({@code debugMode=true}):* <ul>* <li>跳过所有证书验证(存在严重安全风险,仅限测试环境!)</li>* <li>警告:可能导致中间人攻击,绝不能用于生产环境</li>* </ul>* </li>* <li><b>自定义证书</b>({@code trustedCertificates 非空}):* <ul>* <li>仅信任指定的 PEM 格式证书</li>* <li>适用于双向认证或自签名证书场景</li>* </ul>* </li>* <li><b>系统默认</b>(默认模式):* <ul>* <li>使用设备/系统预装的 CA 证书</li>* <li>适用于生产环境的标准 HTTPS 通信</li>* </ul>* </li>* </ul>* </p>** @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 安全组件/*** 调试模式专用:信任所有证书的管理器* <p>* 重写证书验证方法,跳过所有客户端和服务器证书检查* 警告:此实现存在严重安全漏洞,仅限测试环境使用!* </p>*/@Deprecatedprivate static class TrustAllManager implements X509TrustManager {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {// 客户端证书验证(此处无需处理)}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {// 跳过服务器证书验证(调试模式专用)}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0]; // 返回空列表表示接受所有颁发者}}/*** 调试模式专用:创建忽略证书验证的 SSLSocketFactory* <p>* 警告:此模式会绕过所有证书验证,存在中间人攻击风险,仅限测试环境使用!* </p>** @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();}/*** 获取系统默认的证书信任管理器* <p>* 使用设备或系统预装的证书颁发机构 (CA) 列表* </p>** @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 格式)* <p>* 将指定的证书添加到信任列表,用于验证服务器证书* </p>** @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 {@Overridepublic 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;}}/*** 失败重试拦截器(基于默认配置的最大重试次数)* <p>* 实现逻辑:* <ul>* <li>对每个请求尝试 {@code maxRetries + 1} 次(包括首次)</li>* <li>仅处理 {@link IOException}(网络层异常)</li>* <li>成功响应({@code response.isSuccessful()})立即返回</li>* </ul>* </p>*/private static class RetryInterceptor implements Interceptor {private final int maxRetries; // 最大重试次数(不包含首次请求)/*** @param maxRetries 最大重试次数(建议 0-5,默认 3)*/public RetryInterceptor(int maxRetries) {this.maxRetries = maxRetries;}@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();IOException exception = null;for (int i = 0; i <= maxRetries; i++) { // 包括首次请求,总尝试次数 maxRetries + 1try {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 统一响应处理/*** 解析响应并处理异常* <p>* 自动关闭响应体,封装非成功状态码为 {@link HttpException}* </p>** @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 请求)* <p>* POST 提交 JSON 数据示例:* <pre>* 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 以外状态码* </pre>* <p>* GET 请求带查询参数示例:* <pre>* Map<String, String> params = new HashMap<>();* params.put("key1", "value1");* params.put("key2", "value2");* String result = OkHttp_Util.get("https://api.get.com")* .addUrlParam(params) // 批量添加查询参数* .execute_Pro();* </pre>*/public static class RequestChainBuilder {// region MediaType 常量// 定义 JSON 数据的媒体类型,字符集为 UTF-8private static final MediaType JSON_TYPE = MediaType.get("application/json; charset=utf-8");// 定义表单数据的媒体类型,字符集为 UTF-8private static final MediaType FORM_URLENCODED_TYPE = MediaType.get("application/x-www-form-urlencoded; charset=utf-8");// 定义纯文本数据的媒体类型,字符集为 UTF-8private static final MediaType TEXT_PLAIN_TYPE = MediaType.get("text/plain; charset=utf-8");// 定义 XML 数据的媒体类型,字符集为 UTF-8private static final MediaType XML_TYPE = MediaType.get("application/xml; charset=utf-8");// endregionprivate 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() {// 向请求构建器中设置请求方法为 GETrequestBuilder.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-dataMultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);// 遍历参数并添加到 MultipartBody 构建器中params.forEach(bodyBuilder::addFormDataPart);// 构建请求体this.requestBody = bodyBuilder.build();// 返回当前构建器实例,以支持链式调用return this;}/*** 设置 multipart/form-data 表单参数(含文件上传)* <p>* 示例:上传单个文件* <pre>* 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();* </pre>** @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-dataMultipartBody.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;}/*** 执行同步请求并返回原始响应对象。* <p>* 适用于需要手动处理响应状态码和响应体的场景,需调用者手动关闭响应体。** @return OkHttp 的 {@link Response} 对象* @throws IOException 网络异常、请求超时或服务器错误*/public Response execute() throws IOException {// 构建最终URLHttpUrl httpUrl = urlBuilder.build();// POST请求设置请求体if (requestBody != null) {// 若请求体不为空,则设置请求方法为 POST 并设置请求体requestBuilder.method("POST", requestBody);}// 设置URL并构建请求Request request = requestBuilder.url(httpUrl).build();// 执行请求并返回响应return client.newCall(request).execute();}/*** 执行同步请求并返回处理后的响应字符串(自动处理非成功状态码)。* <p>* 功能包括:* <ul>* <li>自动关闭响应体(通过 try-with-resources)</li>* <li>检查响应状态码,非 2xx 状态码抛出 {@link OkHttp_Util.HttpException}</li>* <li>将响应体转换为 UTF-8 编码的字符串</li>* </ul>* 自动关闭响应体,处理非成功状态码({@code response.isSuccessful()})* </p>** @return 响应正文(UTF-8 字符串)* @throws IOException 网络异常、响应体解析失败或 I/O 错误* @throws OkHttp_Util.HttpException 当响应状态码非 2xx 时抛出,包含状态码和错误消息*/public String execute_Pro() throws IOException {// 构建最终URLHttpUrl 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);}}/*** 执行异步请求,通过回调处理响应结果。* <p>* 支持处理成功响应({@link Callback#onResponse(Call, Response)})和失败情况({@link Callback#onFailure(Call, IOException)})。* <p>* **注意**:异步请求不会自动关闭响应体,需在 {@link Callback#onResponse(Call, Response)} 中手动关闭。** @param callback 异步回调接口,不可为 null* @throws NullPointerException 当 callback 为 null 时抛出*/public void enqueue(Callback callback) {try {// 检查 OkHttpClient 实例是否为 nullif (client == null) {// 若为 null,则调用回调的 onFailure 方法并传入异常信息callback.onFailure(null, new IOException("OkHttpClient 实例为 null"));return;}// 构建请求Request request = requestBuilder.url(urlBuilder.build()).build();// 检查请求是否为 nullif (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 实例并设置请求方法为 GETreturn new RequestChainBuilder(url, OkHttp_Util.getDefaultInstance()).get();}/*** 重载工厂方法:创建一个 POST 请求的链式构建器** @param url 请求的 URL* @return 链式构建器实例*/public static RequestChainBuilder post(String url) {// 创建一个 RequestChainBuilder 实例并设置请求方法为 POSTreturn 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();}// endregionpublic 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证书管理
- 健壮性保障:内置重试机制和异常处理
适合中大型项目作为基础网络组件使用,建议根据实际业务需求调整超时时间和重试策略。