ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

RestTemplate实战指南:从基础用法到超时、连接池与避坑经验

RestTemplate实战指南:从基础用法到超时、连接池与避坑经验 RestTemplate 是 Spring 生态里最老牌、也最让人又爱又恨的 HTTP 客户端工具。我在实际项目里用它对接过支付回调、第三方 OpenAPI、内部微服务接口也踩过不少坑。如果你正在写 Java 后端迟早会和 RestTemplate 打交道——尤其在一些老项目里它可能还会继续服役很多年。这篇就把 RestTemplate 怎么用、为什么这样用、有哪些坑一次性说清楚方便你查漏补缺也适合刚接触 Spring 的队友快速上手。我写这篇文章的定位不是贴官方文档而是把我实际开发中沉淀下来的用法和排查思路摊开讲。你看到的不只是 API 调用还有方法选型的逻辑、超时和拦截器的配置姿势、以及遇到乱码和连接池问题时的处理套路。看完之后你会发现 RestTemplate 并没有传说中那么难难的只是把细节串起来。1. RestTemplate 到底是什么为什么还需要学它1.1 一句话本质封装了 HTTP 调用的模板工具RestTemplate 是 Spring 提供的一个同步 HTTP 客户端模板类。所谓“模板”就是帮你把 HTTP 请求的建立、发送、响应解析这些重复代码封装好你只需要告诉它三件事请求地址、请求参数、期望的返回类型。它底层会调用 JDK 的 HttpURLConnection或者 Apache HttpClient、OkHttp 等具体 HTTP 客户端然后把 JSON、XML 等响应体自动转成 Java 对象。这就好比你去餐厅吃饭不需要自己买菜、洗菜、炒菜只需要告诉服务员要什么菜、什么口味、放不放辣后厨会帮你搞定一切。RestTemplate 就是那个服务员你只需要把 URL、参数、返回类型递过去剩下的连接管理、报文转换、异常包装都由它处理。这里有个容易混淆的点RestTemplate 和 HttpClient 不是同一个层面的东西。HttpClient 是底层网络库RestTemplate 是上层封装。RestTemplate 可以通过setRequestFactory方法切换底层实现默认用的是 JDK 的SimpleClientHttpRequestFactory。如果你对性能或连接管理有更高要求可以换成 Apache HttpClient后面我会专门讲。1.2 为什么 Spring 官方推荐 WebClient我还建议你先学会 RestTemplateSpring 5 之后官方主推 WebClient它是响应式非阻塞的 HTTP 客户端能更好地利用系统资源。很多新项目一上来就直接用 WebClient导致一些刚入行的同学以为 RestTemplate 已经过时了。但实际上现有存量项目里 RestTemplate 的使用率依然非常高 Spring Boot 3.x 之前的自动配置也默认支持 RestTemplate 的装配。从学习成本来说RestTemplate 是同步阻塞模型代码写起来非常直观发请求、等响应、拿结果就是正常的 Java 调用思维。WebClient 则需要理解 Reactor 的Mono、Flux异步流调试时链路更长。如果你团队里大多数人还不熟悉响应式编程贸然上 WebClient 反而会降低开发效率。我的建议是如果你维护老项目必须把 RestTemplate 吃透如果你是新项目选型并且没有强烈的吞吐量要求RestTemplate 依然是稳妥的选择。即使你决定用 WebClient了解 RestTemplate 的请求语义和转换机制也会让你更容易理解 Spring 对 HTTP 交互的抽象思路。1.3 适用场景什么时候选 RestTemplate 最合适RestTemplate 特别适合以下几种场景服务端之间同步调用比如订单服务调用用户服务、网关调用认证中心接口数量不多QPS 也不高的场景。调用第三方 HTTP 接口比如微信支付、支付宝、短信平台、OSS 服务等基本都是同步请求用 RestTemplate 写起来最简单。需要快速开发内部管理工具比如定时拉取数据、批量校验资源、写脚本做接口联调。老项目升级维护很多 Spring Boot 2.x 项目已经把 RestTemplate 用在了核心链路里重构成本太高不如把用法规范好。如果你遇到的是高并发、长连接、响应要求极高的场景比如流式推送、WebSocket、大规模的网关聚合那确实应该考虑 WebClient 或底层 HttpClient 定制。但在常规业务系统里 RestTemplate 的同步性能完全够用。2. 快速上手从一个最简单的 GET 请求开始2.1 依赖与基础环境准备如果你使用 Spring Boot不需要额外引入 RestTemplate 的依赖它已经包含在spring-web模块里。只需要在启动类或配置类中声明一个RestTemplateBean 即可。Maven 项目里保证有spring-boot-starter-web就行dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency如果你的项目不是 Spring Boot而是原生 Spring MVC则需要引入spring-web和spring-context。不过现在绝大多数情况都是 Spring Boot启动器直接搞定。这里我建议你使用组件扫描方式注册 Bean而不是每次都手动new RestTemplate()。原因很简单手动 new 出来的实例不会自动加载 Spring 的转换器、消息转换器定制和拦截器后续想统一加日志或改超时配置就得全局搜索到处改。2.2 创建 RestTemplate 实例的三种姿势第一种直接手动创建适合极简单的测试代码。RestTemplate restTemplate new RestTemplate();第二种在配置类中注册 Bean适合大部分项目。Configuration public class RestTemplateConfig { Bean public RestTemplate restTemplate() { return new RestTemplate(); } }第三种使用RestTemplateBuilder这是 Spring Boot 提供的流式构造器适合需要设置超时、拦截器、根 URI 的场景。Configuration public class RestTemplateConfig { Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(3)) .setReadTimeout(Duration.ofSeconds(10)) .build(); } }RestTemplateBuilder的好处是链式调用、语义清晰而且不会破坏 Spring Boot 的自动配置。我更推荐你在真实项目里用第三种方式后面配置拦截器时尤其方便。2.3 第一个 getForObject 请求拿数据就是这么简单假设有一个用户服务接口地址是GET /users/{id}返回 JSON 如下{ id: 1, name: 张三, email: zhangsanexample.com }定义对应的 Java POJOpublic class User { private Long id; private String name; private String email; // 必须有无参构造getter/setter 不能少 }然后发起请求RestTemplate restTemplate new RestTemplate(); String url https://api.example.com/users/{id}; User user restTemplate.getForObject(url, User.class, 1); System.out.println(user.getName());注意 URL 里的{id}是占位符后面的参数1会按顺序填充进去。这种方式比字符串拼接 URL 更安全能自动处理 URL 编码推荐优先使用。getForObject的意思是“我只关心响应体对应的 Java 对象不关心响应头、状态码”。如果你需要拿到响应状态码、响应头做判断就改用getForEntityResponseEntityUser response restTemplate.getForEntity(url, User.class, 1); if (response.getStatusCode().is2xxSuccessful()) { User body response.getBody(); }我平时有一个习惯如果第三方接口文档里没有明确的错误码设计我会先使用getForEntity把状态码和响应体都打出来联调阶段信息量大等接口稳定了再精简成getForObject。3. 常用 API 拆解GET、POST、PUT、DELETE 一次讲明白3.1 GET 请求的完整写法与返回值取舍GET 请求最常用的有三个方法getForObject、getForEntity和exchange。前两个内部逻辑几乎一样只是返回封装不同。exchange是最通用的入口可以指定 HTTP Method、请求头、请求体同时拿到完整响应。带查询参数的 GET 有几个写法// 方式一URI 模板 参数顺序填充 String url https://api.example.com/users?page{page}size{size}; PageResult result restTemplate.getForObject(url, PageResult.class, 1, 20); // 方式二使用 UriComponentsBuilder 构建 URI uri UriComponentsBuilder.fromUriString(https://api.example.com/users) .queryParam(page, 1) .queryParam(size, 20) .build() .encode() .toUri(); PageResult result restTemplate.getForObject(uri, PageResult.class);我推荐方式一代码简短但如果查询参数特别多或者参数值本身包含特殊字符方式二更稳妥。UriComponentsBuilder能够正确处理中文、空格、特殊符号的编码减少乱码和 400 错误。很多新手会踩一个坑直接用String拼接 URL比如url https://api.example.com/users?name name。如果name是中文或包含生成的 URL 很可能非法服务端收到的参数也会错乱。所以只要参数不是纯数字就尽量用模板或 URI 构建器。3.2 POST 请求JSON、表单、对象实体怎么传先看最常见的 JSON 请求。假设接口POST /users需要提交一个用户对象Content-Type 是application/jsonUser newUser new User(); newUser.setName(李四); newUser.setEmail(lisiexample.com); RestTemplate restTemplate new RestTemplate(); User created restTemplate.postForObject(https://api.example.com/users, newUser, User.class);postForObject的第二个参数可以是任意对象RestTemplate 会通过消息转换器把它序列化成 JSON。默认使用MappingJackson2HttpMessageConverter也就是底层的 Jackson 库。如果你传的是一个Map同样也能转成 JSONMapString, Object body new HashMap(); body.put(name, 王五); body.put(email, wangwuexample.com); String result restTemplate.postForObject(url, body, String.class);需要发送表单格式时不能直接传普通对象需要构造MultiValueMapMultiValueMapString, String formData new LinkedMultiValueMap(); formData.add(username, admin); formData.add(password, 123456); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntityMultiValueMapString, String entity new HttpEntity(formData, headers); String result restTemplate.postForObject(url, entity, String.class);这里的关键点是MultiValueMap而不是普通的HashMap因为HashMap一个 key 只能对应一个 value表单场景经常需要同名参数重复提交MultiValueMap更合适。另外如果你不主动设置 Content-TypeRestTemplate 会把对象按 JSON 处理那第三方接口可能直接报415 Unsupported Media Type。3.3 PUT / DELETE / PATCH 的注意点PUT 和 POST 的写法很像只是方法名不同而且没有putForObject只有put。User updateInfo new User(); updateInfo.setName(张三丰); restTemplate.put(https://api.example.com/users/{id}, updateInfo, 1);注意put方法返回void如果你想拿到响应体或状态码需要手动用exchangeResponseEntityUser response restTemplate.exchange( https://api.example.com/users/{id}, HttpMethod.PUT, new HttpEntity(updateInfo), User.class, 1);DELETE 更简单大多数情况不需要请求体restTemplate.delete(https://api.example.com/users/{id}, 1);如果用exchange发 DELETE 也能实现但不建议无脑用。同步阻塞调用中语义清晰非常重要别贪图一个exchange搞定所有方法代码可读性会下降。PATCH 在 RestTemplate 中没有专门方法可以用exchange指定HttpMethod.PATCH。但这里有个雷默认的SimpleClientHttpRequestFactory基于HttpURLConnection它可能不支持 PATCH 方法某些 JDK 版本会抛异常。解决方案是把底层工厂换成 Apache HttpClient 或 OkHttp。这是我在实际项目里踩过的坑后面常用问题部分还会展开。3.4 exchange 和 execute万能入口exchange是 RestTemplate 所有请求方法的底层实现也是我处理复杂请求的首选。它能让你自由指定请求方法、请求头、请求体和响应类型非常灵活。举个例子对接需要 token 认证的接口HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(token); MapString, Object body new HashMap(); body.put(orderId, 12345); HttpEntityMapString, Object request new HttpEntity(body, headers); ResponseEntityOrderResult response restTemplate.exchange( https://api.example.com/orders/{orderId}/pay, HttpMethod.POST, request, OrderResult.class, 12345);execute比exchange更底层需要传入RequestCallback和ResponseExtractor适合流式下载、超大报文处理等定制场景。但日常业务中exchange已经足够不建议轻易下沉到execute。4. 高级配置与实战细节拦截器、超时、文件上传4.1 超时设置别用默认值否则线上会“卡死”RestTemplate 默认的SimpleClientHttpRequestFactory连接超时和读取超时都是无穷大也就是不超时。这意味着如果第三方接口一直不返回你的线程会一直阻塞最终把线程池占满服务雪崩。所以任何生产环境使用都必须显式配置超时。通过RestTemplateBuilder设置最简单Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(3)) .setReadTimeout(Duration.ofSeconds(5)) .build(); }如果你需要针对不同接口使用不同超时可以创建多个 RestTemplate Bean或者用更底层的SimpleClientHttpRequestFactorySimpleClientHttpRequestFactory factory new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(3000); factory.setReadTimeout(5000); RestTemplate restTemplate new RestTemplate(factory);超时参数不是越小越好。连接超时可以设置短一些比如 2-3 秒毕竟网络连通后建立连接通常很快读取超时要看业务接口的实际耗时例如文件下载、复杂报表查询可能需要 30 秒以上。我一般会先在联调环境压测一轮再根据 P99 耗时设置合理阈值。4.2 用拦截器统一打日志、加签名ClientHttpRequestInterceptor是 RestTemplate 非常强大的扩展点。在请求发送之前你可以读取请求头、请求体在响应返回之后你可以读取响应状态和响应体。利用这个特性可以统一做三件事日志记录、认证签名、请求去重。日志拦截器示例public class LoggingInterceptor implements ClientHttpRequestInterceptor { Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { System.out.println(请求地址: request.getURI()); System.out.println(请求方法: request.getMethod()); System.out.println(请求体: new String(body, StandardCharsets.UTF_8)); ClientHttpResponse response execution.execute(request, body); System.out.println(响应状态: response.getStatusCode()); return response; } }注册到 RestTemplateRestTemplate restTemplate new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList(new LoggingInterceptor()));如果使用RestTemplateBuilder可以这样RestTemplate restTemplate builder .additionalInterceptors(new LoggingInterceptor()) .build();加签名的拦截器也很常见。假设每个请求都需要在 Header 里加上appId和sign你可以在拦截器里统一计算签名业务代码就不用到处重复。实际项目中我习惯在拦截器里只记录requestId、URI、耗时和状态码避免把敏感字段打印出来。如果请求体很大或者包含用户隐私务必脱敏或忽略。4.3 文件上传与下载的 multipart 处理用 RestTemplate 做文件上传比较容易踩坑。核心还是构造MultiValueMap但值类型要用FileSystemResource或ByteArrayResource。上传单个文件HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMapString, Object body new LinkedMultiValueMap(); body.add(file, new FileSystemResource(/tmp/test.pdf)); body.add(remark, 测试上传); HttpEntityMultiValueMapString, Object requestEntity new HttpEntity(body, headers); String result restTemplate.postForObject(https://api.example.com/upload, requestEntity, String.class);如果你需要把内存中的字节流传给接口而不是磁盘文件用ByteArrayResource但要重写getFilename()方法否则很多服务端会忽略文件名ByteArrayResource fileResource new ByteArrayResource(fileBytes) { Override public String getFilename() { return report.xlsx; } }; body.add(file, fileResource);下载文件则可以直接把响应体转成byte[]ResponseEntitybyte[] response restTemplate.exchange( https://api.example.com/download/ fileId, HttpMethod.GET, null, byte[].class); Files.write(Paths.get(/tmp/ fileId), response.getBody());这里要注意下载大文件时如果把整个响应体读进byte[]可能会 OOM。这种情况应改用execute配合流式读取或者直接使用RestTemplate的ResponseExtractor逐块写入文件。日常小文件用byte[]没问题超过 100MB 一定要谨慎。4.4 动态 URL 与 ParameterizedTypeReferenceRestTemplate 支持在请求时再拼接 URL最简单的做法是String url serviceUrl /users/ userId;但这样不够优雅。更好的做法是使用UriComponentsBuilder动态构建URI uri UriComponentsBuilder .fromUriString(serviceUrl) .path(/users/{userId}/orders) .queryParam(status, status) .buildAndExpand(userId) .encode() .toUri();ParameterizedTypeReference用来处理泛型返回值。比如接口返回ListUser如果你直接用List.classRestTemplate 会把 JSON 反序列化成ListLinkedHashMap取值时还要手动转换。用ParameterizedTypeReference能直接得到ListUserParameterizedTypeReferenceListUser typeRef new ParameterizedTypeReferenceListUser() {}; ResponseEntityListUser response restTemplate.exchange( https://api.example.com/users/list, HttpMethod.GET, null, typeRef);注意后面那个花括号不能少这是匿名内部类的语法Java 通过它保存泛型信息。很多老手也会在这里犯糊涂直接把new ParameterizedTypeReferenceListUser()写成普通实例化然后发现类型信息丢失反序列化结果不符合预期。5. 常见问题与避坑经验这些坑我基本都踩过5.1 乱码问题StringHttpMessageConverter 的坑RestTemplate 默认的StringHttpMessageConverter使用 ISO-8859-1 编码而很多接口返回的是 UTF-8。直接拿 String 接收响应时中文可能出现乱码。这个问题在旧版本 Spring 里非常常见现在 Spring Boot 2.x 之后的默认配置有所改善但如果你手动 newRestTemplate()或者调整了消息转换器顺序仍可能触发。解决方案是在配置 RestTemplate 时修改StringHttpMessageConverter的默认字符集ListHttpMessageConverter? converters restTemplate.getMessageConverters(); for (HttpMessageConverter? converter : converters) { if (converter instanceof StringHttpMessageConverter) { ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8); } }如果你用RestTemplateBuilder可以这样RestTemplate restTemplate builder .messageConverters(converters - { converters.forEach(c - { if (c instanceof StringHttpMessageConverter) { ((StringHttpMessageConverter) c).setDefaultCharset(StandardCharsets.UTF_8); } }); }) .build();还有一个容易被忽视的点即使服务端返回了Content-Type: application/json; charsetUTF-8如果响应的字节流本身没有 BOM某些序列化框架也可能识别错编码。最好的方式是在接口设计和联调阶段就确认响应头中的 charset而不是只看字符串乱不乱。5.2 连接池与 HttpClient 的适配默认的SimpleClientHttpRequestFactory每次请求都会新建连接不会复用性能一般。高并发场景下频繁创建 TCP 连接不仅慢还可能耗尽本机端口。要使用连接池通常引入 Apache HttpClient然后配置HttpComponentsClientHttpRequestFactorydependency groupIdorg.apache.httpcomponents/groupId artifactIdhttpclient/artifactId /dependency配置示例CloseableHttpClient httpClient HttpClients.custom() .setMaxConnTotal(200) .setMaxConnPerRoute(50) .evictIdleConnections(Duration.ofSeconds(30)) .build(); HttpComponentsClientHttpRequestFactory factory new HttpComponentsClientHttpRequestFactory(httpClient); factory.setConnectTimeout(3000); factory.setReadTimeout(5000); RestTemplate restTemplate new RestTemplate(factory);这里有几个参数需要解释。setMaxConnTotal是连接池最大连接数setMaxConnPerRoute是每个路由的最大连接数。路由可以理解为“目标域名端口”如果第三方有多个域名需要给每个域名合理分配连接数避免一个域名把连接池占满。如果你没有特殊需求直接用 Apache HttpClient 即可这是目前最主流的 RestTemplate 底层搭配。如果用的是 OkHttp也有对应的OkHttp3ClientHttpRequestFactory但 Spring Boot 自动配置里支持度不如 Apache HttpClient 高。5.3 重试机制怎么做最稳妥RestTemplate 本身没有内置重试机制需要自己封装。最简单的封装是方法级别重试public T T postWithRetry(String url, Object request, ClassT responseType, int retryCount) { int attempt 0; while (attempt retryCount) { try { return restTemplate.postForObject(url, request, responseType); } catch (ResourceAccessException e) { attempt; if (attempt retryCount) { throw e; } try { Thread.sleep(500L * attempt); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); throw new RuntimeException(ex); } } } return null; }这里我故意捕获ResourceAccessException因为超时、连接失败这类 I/O 异常是网络层面的重试有意义而 4xx、5xx 错误比如参数不对、鉴权失败属于业务异常重试不但没用还可能把问题放大。如果你用的是 Spring Retry可以更优雅地声明式重试但引入额外依赖也需要权衡。我的经验是重试次数不要超过 3 次退避策略用固定间隔或指数退避都行但一定要加上最大间隔限制否则接口持续超时时重试请求会把自己服务拖垮。5.4 自签名证书与 RestTemplate 的兼容处理对接内部测试环境或网关系有时会遇到自签名证书问题默认的 RestTemplate 会抛出SSLHandshakeException。解决思路有两种第一种把证书导入 JVM 的信任库。这是最正规的方式适合测试环境固定证书的情况。第二种在代码里自定义信任管理器跳过证书校验。这种方式只建议在本地测试或非生产环境使用安全风险很高。SSLContext sslContext SSLContexts.custom() .loadTrustMaterial(null, (chain, authType) - true) .build(); CloseableHttpClient httpClient HttpClients.custom() .setSSLContext(sslContext) .build(); HttpComponentsClientHttpRequestFactory factory new HttpComponentsClientHttpRequestFactory(httpClient); RestTemplate restTemplate new RestTemplate(factory);我个人不推荐在生产环境跳过证书校验尤其是涉及资金、用户数据等敏感接口。如果生产要用自签名证书更合理的方案是双方协商后将证书部署到网关层由统一的网关完成 TLS 终结业务系统不直接接触证书细节。5.5 常见异常速查表把我在实践中遇到的高频异常整理如下异常信息原因排查方向ResourceAccessException连接超时、读取超时、网络断开检查目标地址是否可达、端口是否开放、超时配置是否合理HttpMessageNotReadableException响应体无法反序列化为目标类型检查 JSON 结构与 POJO 是否匹配、是否有未知字段或类型不兼容HttpClientErrorException$Unauthorized401 鉴权失败检查 token 是否过期、Header 是否正确HttpServerErrorException$InternalServerError服务端 500结合服务端日志定位重试通常无效UnknownContentTypeException响应 Content-Type 无法匹配转换器检查响应头是否缺失、是否被网关包装成其它类型RestClientException: URI is not absoluteURL 拼接错误检查地址是否缺少http://或https://前缀遇到异常时我建议第一步不是去改代码而是把完整的请求 URL、请求头和方法打出来很多时候问题出在参数拼写或编码上。RestTemplate 的异常信息通常已经包含服务端返回的响应体仔细看往往能直接定位。5.6 一个容易忽略的小细节请求体过大导致的性能问题RestTemplate 默认会把请求体读入内存再发送。如果你传输的是超大 JSON比如上百 MB可能会把内存打满。一般业务系统里不会出现这种情况但如果你在做数据同步、批量导入接口就要特别注意。比较好的做法是把数据分批传输每次控制在 1MB-5MB 之间实在不行再考虑换用WebClient或手动实现流式上传。还有个小技巧不要把byte[]直接放在HttpEntity里发送如果服务端接口接收的是 JSON 字符串最好用String构造请求体这样能避免一次隐式的字节拷贝。6. 我常用的封装思路让团队少走弯路在实际项目里我通常不会让业务代码直接使用RestTemplate而是再包一层HttpClientWrapper把日志、鉴权、异常转换、重试都统一收敛到里面。这样做的好处是第三方的接口调用在团队里只有一种正确姿势哪怕新同学不熟悉 RestTemplate 也能快速上手。简单示例Service public class ApiClientService { private final RestTemplate restTemplate; public ApiClientService(RestTemplate restTemplate) { this.restTemplate restTemplate; } public T T execute(String url, HttpMethod method, Object body, ClassT responseType) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntityObject entity new HttpEntity(body, headers); return restTemplate.exchange(url, method, entity, responseType).getBody(); } }有签名或 token 需求时在execute里统一设置即可。之后如果切到 WebClient只需要改动这层封装上游业务代码不需要大幅变动。我一直认为工具类要解决的问题是“让大多数人不犯错”而不是“功能最多的炫技工具”。另外如果你们项目里有多个第三方平台建议把每个平台的RestTemplate单独命名为xxxRestTemplate并且配置不同的拦截器、超时和底层连接池。这样能避免一个平台接口超时影响到另一个平台的正常请求。我在早期项目里把所有调用共用同一个 RestTemplate结果第三方 A 接口变慢时第三方 B 的请求也被拖慢后来拆分了实例才根治。许多人对 RestTemplate 的疑虑来自它在异步和性能上的短板但回到业务系统本身同步调用依然是主流形态。先把 RestTemplate 用熟练再去看 WebClient 的异步模型你会发现自己对 Spring 整个 HTTP 抽象体系的理解会清晰很多。我后续还会补一篇更偏源码和底层实现的文章如果你想了解消息转换器、请求工厂的加载逻辑或者想看 WebClient 的迁移思路可以留意后面的系列。
返回列表