当前位置: 首页 > 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.cnnetsun.cn/news/860569.html

相关文章:

  • 手把手教你封装一个树形结构处理类(Java 通用 Tree 工具,支持无限层级)
  • 【计算机网络】ep1:物理层概述
  • 百度开源上传组件在局域网如何处理大文件断点续传?
  • PostgreSQL MCP
  • 高危!Apache Parquet Java库曝远程代码执行(RCE)漏洞,需立即修复
  • 大模型工具使用指南:MCP与Skills对比分析,收藏级技术解析
  • 从0到1掌握RAG:解决大模型落地痛点的终极方案,建议收藏!
  • Nodejs+vuenet基于位置管理的企业 员工考勤打卡系统设计app小程序
  • 我让AI读了产品PRD,自动生成“验收标准”测试用例
  • 2026年效率翻倍:开发者必装的AI助手APP
  • AI测试用例生成的“异常流”缺失:一场未被教导的盲区
  • ‌AI驱动的测试用例模板统一实践:从标准框架到团队协同的完整路径
  • 基于AI课堂+Spring Boot +Vue的面向中职学校的第二课堂教学管理系统 毕业设计项目实战辅导指导
  • 基于人工智能AI + Spring Boot + AI建议分析建筑工程项目管理系统 毕业设计项目实战辅导指导
  • 为什么工业智能化需要工业AI大脑?应该如何选择?
  • 别再“假装数字化”了!3分钟搞懂:什么叫“数字化成效”,以及怎么用最少的钱干最靓的事!
  • Nodejs+vue新闻订阅推荐系统头条app的设计与实现 小程序
  • 看懂风扇的“里外”:原理、构造、性能与计算的系统性解读
  • 10个技巧:用AI自动生成测试报告
  • 【值得珍藏】LLM推理优化技术详解:从数据级到系统级的全面解析
  • 9999999
  • AI创作避坑 学术党实测有效,免费搞定查重+绘图+改稿
  • 收藏必看!告别RAG碎片化:一文讲透Forms-Dynamics框架下的Agent记忆系统
  • 收藏!AI大模型应用开发工程师全解析,小白程序员必看的入行指南
  • Pelco KBD300A 模拟器:17.按照pytest自动化测试方案规划建立测试基础框架
  • 听说现在JDBC已经过时了,还需要学吗?
  • 开题报告被批 “无逻辑、缺创新”?虎贲等考 AI 一键解锁 “过审密码”
  • Nodejs+vueAndroid的垃圾分类系统小程序
  • 我的区块链运维日记 · 第 12 日:消失的服务器 —— 也就是我们如何被 IPFS 逼疯的
  • nginx和openresty和apisix区别