当前位置: 首页 > news >正文

手把手教你封装一个调用三方接口的 HTTP 工具类(Spring Boot + Java 实战)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!


🧩 一、为什么需要封装 HTTP 工具类?

在实际开发中,我们经常要调用第三方接口,比如:

  • 支付宝/微信支付回调;
  • 短信平台发送验证码;
  • 对接 ERP、CRM 系统;
  • 调用 AI 大模型 API。

如果每次都写HttpURLConnectionRestTemplate的样板代码,不仅重复、难维护,还容易出错(超时、重试、日志、异常处理等)。

封装目标

  • 统一超时控制;
  • 自动重试机制;
  • 请求/响应日志记录;
  • 异常统一处理;
  • 支持 JSON、Form 表单等多种格式;
  • 易于单元测试。

🛠️ 二、使用 Spring Boot 推荐方案:RestTemplate+ 封装

💡 虽然 Spring 6+ 推出了WebClient(响应式),但大多数项目仍用RestTemplate,本文以它为基础。

1. 添加依赖(Spring Boot 默认已包含)

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

2. 创建 HTTP 工具类(核心!)

import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Map; @Component public class HttpUtil { private static final Logger log = LoggerFactory.getLogger(HttpUtil.class); private final RestTemplate restTemplate; private final ObjectMapper objectMapper; // 注入 Spring 管理的 RestTemplate 和 ObjectMapper public HttpUtil(RestTemplate restTemplate, ObjectMapper objectMapper) { this.restTemplate = restTemplate; this.objectMapper = objectMapper; } /** * POST JSON 请求 */ public <T> T postJson(String url, Object requestBody, Class<T> responseType) { return doRequest(url, HttpMethod.POST, buildJsonEntity(requestBody), responseType); } /** * GET 请求(带 Query 参数) */ public <T> T get(String url, Map<String, Object> params, Class<T> responseType) { String urlWithParams = appendQueryParams(url, params); return doRequest(urlWithParams, HttpMethod.GET, null, responseType); } /** * POST Form 表单 */ public <T> T postForm(String url, Map<String, String> formData, Class<T> responseType) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.setAll(formData); HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers); return doRequest(url, HttpMethod.POST, entity, responseType); } // ------------------ 内部方法 ------------------ private <T> HttpEntity<T> buildJsonEntity(T body) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); return new HttpEntity<>(body, headers); } private String appendQueryParams(String url, Map<String, Object> params) { if (params == null || params.isEmpty()) return url; StringBuilder sb = new StringBuilder(url); if (!url.contains("?")) sb.append("?"); else if (!url.endsWith("&")) sb.append("&"); params.forEach((k, v) -> sb.append(k).append("=").append(v).append("&")); return sb.substring(0, sb.length() - 1); // 去掉最后一个 & } private <T> T doRequest(String url, HttpMethod method, HttpEntity<?> entity, Class<T> responseType) { long start = System.currentTimeMillis(); try { // 记录请求日志 log.info(">>> HTTP {} Request: {}", method, url); if (entity != null && entity.getBody() != null) { String bodyStr = objectMapper.writeValueAsString(entity.getBody()); log.info(">>> Request Body: {}", bodyStr); } ResponseEntity<T> response = restTemplate.exchange(url, method, entity, responseType); // 记录响应日志 long cost = System.currentTimeMillis() - start; log.info("<<< HTTP Response Status: {}, Cost: {}ms", response.getStatusCode(), cost); if (response.getBody() != null) { log.debug("<<< Response Body: {}", response.getBody()); } return response.getBody(); } catch (ResourceAccessException e) { log.error("HTTP 请求超时或网络异常 | URL: {} | Error: {}", url, e.getMessage()); throw new RuntimeException("调用三方接口超时", e); } catch (RestClientException e) { log.error("HTTP 客户端错误 | URL: {} | Error: {}", url, e.getMessage()); throw new RuntimeException("调用三方接口失败", e); } catch (Exception e) { log.error("HTTP 请求未知异常 | URL: {} | Error: {}", url, e.getMessage(), e); throw new RuntimeException("调用三方接口异常", e); } } }

⚙️ 三、配置 RestTemplate(带超时和重试)

1. 自定义 RestTemplate Bean

@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(5000); // 连接超时 5s factory.setReadTimeout(10000); // 读取超时 10s factory.setConnectionRequestTimeout(3000); // 从连接池获取连接超时 RestTemplate restTemplate = new RestTemplate(factory); // 可选:添加拦截器(如统一 header) return restTemplate; } }

🔔 需要额外依赖(用于设置超时):

<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>

🧪 四、使用示例

场景 1:调用支付回调接口(POST JSON)

@Service public class PaymentService { @Autowired private HttpUtil httpUtil; public void notifyPayment(String orderId) { Map<String, Object> request = Map.of( "orderId", orderId, "status", "SUCCESS" ); // 调用三方 String result = httpUtil.postJson("https://partner.com/api/notify", request, String.class); log.info("通知结果: {}", result); } }

场景 2:获取用户信息(GET 带参数)

Map<String, Object> params = Map.of("userId", "12345", "token", "abc"); User user = httpUtil.get("https://api.example.com/user", params, User.class);

场景 3:发送短信(POST Form)

Map<String, String> form = Map.of( "mobile", "13800138000", "content", "您的验证码是:123456" ); String resp = httpUtil.postForm("https://sms.provider.com/send", form, String.class);

❌ 五、反例 & 常见错误

反例 1:每次 new RestTemplate(无法复用连接池)

// ❌ 错误!每次创建新实例,性能差,无超时控制 RestTemplate rt = new RestTemplate(); rt.postForObject(url, body, String.class);

✅ 正确:注入 Spring 管理的 Bean


反例 2:不处理异常,直接吞掉

try { restTemplate.getForObject(url, String.class); } catch (Exception e) { // 啥也不干 😱 }

💥 后果:线上故障无法排查!

✅ 正确:记录日志 + 抛出业务异常


反例 3:敏感信息打印到日志

log.info("请求体: {}", body); // 包含密码、token!

✅ 建议:

  • 生产环境debug级别才打 body;
  • 敏感字段脱敏(如password=***)。

⚠️ 六、进阶建议(生产级)

功能实现方式
重试机制使用Spring RetryResilience4j
熔断降级集成 Sentinel / Hystrix
Metrics 监控记录调用次数、耗时、成功率(Micrometer)
Mock 测试使用WireMock模拟三方接口
HTTPS 双向认证配置 SSLContext(金融场景)

🎯 七、总结

封装后的HttpUtil具备:

  • ✅ 统一入口,调用简单;
  • ✅ 自动日志(请求/响应/耗时);
  • ✅ 超时控制;
  • ✅ 异常包装;
  • ✅ 支持多种格式(JSON/Form/GET);
  • ✅ 易于扩展(加签名、加 header 等)。

从此告别“复制粘贴式” HTTP 调用!


视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

http://www.jsqmd.com/news/316952/

相关文章:

  • 【计算机网络】ep1:物理层概述
  • Spring Boot 中如何使用自定义配置管理器?告别硬编码,优雅管理你的配置!
  • Nacos 工作原理你真的了解吗?从配置拉取到动态刷新,一文讲透!
  • 【计算机网络】ep0:计算机网络概述
  • Nacos 你真的了解吗?Spring Boot 集成配置中心实战指南(小白也能看懂!)
  • 枚举类 enum class:强类型枚举的优势
  • CTF之——密码破解工具hashcat,零基础入门到精通,看完这篇就足够了~
  • 国内AI编程IDE对比(二):从零构建桌面应用实测
  • Java类型转换
  • 对目前C++方向的一些想法
  • 基础架构即代码?不,Sealos 让基础架构变成了开箱即用
  • 我终于不用在周末处理集群故障了,感谢 Sealos 的架构设计
  • 【26美赛B题】2026美赛数学建模(MCM/ICM)思路解析及代码分享
  • 【26美赛C题】2026美赛数学建模(MCM/ICM)思路解析及代码分享
  • msvcr80d.dll文件丢失找不到问题 免费下载方法分享
  • 国产化系统中WebUploader如何处理局域网大文件断点续传?
  • 百度开源上传组件在局域网如何处理大文件断点续传?
  • 局域网内WebUploader怎样支持大文件分段与断点续传?
  • 浙江万全扑克有限公司 联系方式:背景与联系信息参考
  • 2025环境试验设备厂商大比拼,口碑出炉,盐水喷雾试验箱及各种老化房,环境试验设备生产厂家排行榜
  • 聊聊杭州比较不错的职业装定制专业公司,哪家性价比高看这里
  • 聊聊煜形象个人西服定制职业装定制靠不靠谱,费用多少
  • 聊聊印刷胶辊定制厂家,泰兴金茂辊业区口碑如何
  • 浙江万全扑克有限公司 联系方式:产品选购与使用通用指南
  • 驰创轴承性价比怎么样,看它口碑与核心竞争力表现
  • 平面设计公司价格怎么算,全速网络收费标准是啥?
  • 浙江万全扑克有限公司 联系方式:官方信息查询与核实指引
  • 2026年目前评价高的不锈钢管定制批发怎么选择,不锈钢彩色板/不锈钢扁钢/不锈钢槽钢,不锈钢管厂商口碑推荐
  • msvcp120.dll文件丢失在系统内 如何修复? 免费下载方法分享
  • msvcp140.dll文件丢失在系统 打不开程序 免费下载方法分享