核心内容摘要
PCI!PciSetResources函数分析之PdoExtension->IDEInNativeMode----server03需修改删除【debug模式下】
视频看了几百小时还迷糊关注我几分钟让你秒懂
为什么需要封装 HTTP 工具类在实际开发中我们经常要调用第三方接口比如支付宝/微信支付回调短信平台发送验证码对接 ERP、CRM 系统调用 AI 大模型 API。
如果每次都写HttpURLConnection或RestTemplate的样板代码不仅重复、难维护还容易出错超时、重试、日志、异常处理等。
✅封装目标统一超时控制自动重试机制请求/响应日志记录异常统一处理支持 JSON、Form 表单等多种格式易于单元测试。
️
使用 Spring Boot 推荐方案RestTemplate 封装 虽然 Spring 6 推出了WebClient响应式但大多数项目仍用RestTemplate本文以它为基础。
添加依赖Spring Boot 默认已包含dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency
创建 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, ClassT responseType) { return doRequest(url, HttpMethod.POST, buildJsonEntity(requestBody), responseType); } /** * GET 请求带 Query 参数 */ public T T get(String url, MapString, Object params, ClassT responseType) { String urlWithParams appendQueryParams(url, params); return doRequest(urlWithParams, HttpMethod.GET, null, responseType); } /** * POST Form 表单 */ public T T postForm(String url, MapString, String formData, ClassT responseType) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMapString, String map new LinkedMultiValueMap(); map.setAll(formData); HttpEntityMultiValueMapString, String entity new HttpEntity(map, headers); return doRequest(url, HttpMethod.POST, entity, responseType); } // ------------------ 内部方法 ------------------ private T HttpEntityT 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, MapString, 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() -
; // 去掉最后一个 } private T T doRequest(String url, HttpMethod method, HttpEntity? entity, ClassT 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); } ResponseEntityT 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带超时和重试
自定义 RestTemplate BeanConfiguration public class RestTemplateConfig { Bean public RestTemplate restTemplate() { HttpComponentsClientHttpRequestFactory factory new HttpComponentsClientHttpRequestFactory(); factory.setConnectTimeout(
; // 连接超时 5s factory.setReadTimeout(
; // 读取超时 10s factory.setConnectionRequestTimeout(
; // 从连接池获取连接超时 RestTemplate restTemplate new RestTemplate(factory); // 可选添加拦截器如统一 header return restTemplate; } } 需要额外依赖用于设置超时dependency groupIdorg.apache.httpcomponents/groupId artifactIdhttpclient/artifactId /dependency
使用示例场景 1调用支付回调接口POST JSONService public class PaymentService { Autowired private HttpUtil httpUtil; public void notifyPayment(String orderId) { MapString, 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 带参数MapString, Object params Map.of(userId, 12345, token, abc); User user httpUtil.get(https://api.example.com/user, params, User.class);场景 3发送短信POST FormMapString, 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 Retry或Resilience4j熔断降级集成 Sentinel / HystrixMetrics 监控记录调用次数、耗时、成功率MicrometerMock 测试使用WireMock模拟三方接口HTTPS 双向认证配置 SSLContext金融场景
七、
总结封装后的HttpUtil具备✅ 统一入口调用简单✅ 自动日志请求/响应/耗时✅ 超时控制✅ 异常包装✅ 支持多种格式JSON/Form/GET✅ 易于扩展加签名、加 header 等。
从此告别“复制粘贴式” HTTP 调用视频看了几百小时还迷糊关注我几分钟让你秒懂