史上最烂 spring RestTemplate 原理分析

The worst《spring RestTemplate》in history

  Spring RestTemplate 概述、核心特性、模板方法、底层实现、核心组件类图、核心组件简述、常用 HTTP 方法使用示例、与 WebClient 区别。


—— 2025 年 5 月 5 日 甲辰年四月初八 立夏

版本

  • jdk:17
  • spring:6.1.3
  • spring boot:3.2.2

1 概述

  RestTemplate 是 spring framework 框架提供的一个同步的 HTTP 客户端工具类,属于 spring web 模块的一部分。它简化了与 HTTP 服务器的交互,为 RESTful 风格服务调用提供了模板化的方法。

1.1 核心特性
  • 模板化设计:提供常见 HTTP 操作(如 GET、POST 等)的快捷方法。
  • 同步请求:所有方法都是阻塞式的,会等待服务器响应。
  • 消息转换:会自动处理请求和响应的序列化与反序列化。
  • 线程安全:RestTemplate 实例是线程安全的,可在多个线程之间共享。
  • 异常转换:会将 HTTP 错误转换为 spring 的异常体系。
  • 灵活配置:支持多种底层 HTTP 客户端实现。
1.2 模版方法

  RestTemplate 为每种 HTTP 方法提供了多种便捷使用方法:

  • GET:getForObject()、getForEntity()
  • POST:postForObject()、postForEntity()、postForLocation()
  • PUT:put()
  • DELETE:delete()
  • PATCH:patchForObject()
  • HEAD:headForHeaders()
  • OPTIONS:optionsForAllow()
  • 通用方法:exchange()、execute()
1.3 底层实现

  RestTemplate 底层依赖于其他 HTTP 客户端实现,提供了 ClientHttpRequestFactory 接口作为其它组件与 spring web 集成的规范。常见的底层 HTTP 客户端实现有:

  • JDK 标准 HttpURLConnection(若为引入其它实现,则默认使用该实现)。
  • Apache HttpClient
  • OkHttp
  • Netty
  • Jetty
  • 其它实现了 ClientHttpRequestFactory 接口的客户端。

2 核心组件

2.1 核心组件类图

rest-resttemplate

2.2 核心组件简述

  RestTemplate 相关类组件简要说要及相关关系如下:

  • RestOperations:该接口规定了 RESTful 操作的基础集合,由 RestTemplate 实现此接口,通常不直接使用。

      如 getForObject()、getForEntity()、postForObject()、postForObject()、exchange()、execute() 方法等。

  • RestTemplate:实现了 RestOperations 接口,具体实现了 RESTful 接口调用的各类方法。

    • 该类通过聚合 ClientHttpRequestFactory 类来实现请求对象的创建及调用。
    • 该类通过聚合 ResponseExtractor 类来实现响应结果的获取。
    • 该类通过聚合 HttpMessageConverter 类来协助 ResponseExtractor 对响应结果进行消息转换。
    • 该类通过聚合 ResponseErrorHeandler 来将 HTTP 错误转换为 spring 异常(IOException)。
  • ClientHttpRequestFactory:HTTP 客户端工厂接口。其声明了 createRequest() 方法,该方法用来创建 ClientHttpRequest 实例。若要在 spring web 中集成其它第三方 HTTP 客户端或自定义实现 HTTP 客户端则需实现该接口。同时,spring web 为我们提供了主流 HTTP 客户端库的实现:

    • SimpleClientHttpRequestFactory:针对 JDK(HttpURLConnection)标准库的实现。注:默认使用该实现。
    • HttpComponentsClientHttpRequestFactory:针对 Apache 库的实现。
    • OkHttp3ClientHttpRequestFactory:针对 OkHttp 库的实现。注:该实现在 spring 6.1 版中已弃用,在 6.2 版本中将移除。
    • Netty4ClientHttpRequestFactory:针对 Netty4 库的实现。注:该实现在 spring 5.0 中已弃用,推荐使用响应式客户端 ReactorClientHttpConnector 代替。
    • JettyClientHttpRequestFactory:针对 Jetty 库的实现。
  • ClientHttpRequest:http 客户端请求体。该接口声明了 execute() 方法,用来执行 http 请求,并返回响应结果 ClientHttpResponse 对象,同时抛出 IOException 异常。

    • 其实例由 ClientHttpRequestFactory 工厂创建。
    • 与 ClientHttpRequestFactory 相同,针对不同 HTTP 客户端库有不同的实现,默认使用 SimpleClientHttpRequest 实现。
  • ClientHttpResponse:http 客户端响应体。表示 http 请求的响应结果,包括 HTTP 状态码、响应消息等。

  • ResponseExtractor:响应提取器,声明了 extractData() 方法,负责从 ClientHttpResponse 中提取中真正的响应数据(即业务数据)。

    • 该类通过聚合 HttpMessageConverter 类来将 ClientHttpResponse 中的响应流数据转换为目标格式。
  • ResponseErrorHandler:响应错误处理器,负责将 http 响应错误转换为 spring IOException 异常。

    • 该类通过聚合 HttpMEssageConverter 类来进行异常转换。
  • HttpMessageConverter:http 消息转换器。负责处理请求/响应的消息转换,即消息的读写操作或序列化与反序列化。

    • 在消息转换时,会涉及到 MediaType 的选择顺序问题:
      • 首先看 @RequestMapping 注解上是否指定(如 produces = “application/json”),亦或是 response 的 ContentType 属性。
      • 其次看 request 的 header 中是否指定 Accept。
      • 最后看 HttpMessageConverter 的先后顺序,谁支持就使用谁。
    • 对于 json 消息的转换,其底层依赖于各种序列化/反序列化库,如 jackson、gson、fastjson 等,同时,spring web 针对不同库提供了消息转换器具体实现:
      • MappingJackson2HttpMessageConverter:利用 jackson 库实现的消息转换器。注:spring 框架中默认使用 jackson 进行序列化/反序列化。
      • GsonHttpMessageConverter:利用 Google Gson 库实现的消息转换器。
      • FastJsonHttpMessageConverter:alibaba 基于 fastjson 库实现的消息转换器。注:该实现由 alibaba 提供,并非 spring web。
      • ByteArrayHttpMessageConverter:针对字节数组实现的消息转换器。读取支持所有类型,写入支持二进制流类型,即 Content-Type: application/octet-stream。
      • StringHttpMessageConverter:针对字符串实现的消息转换器。读取支持所有类型,写入支持纯文本类型,即 Content-Type: text/plain。
  • RestTemplateBuilder:由 spring boot 提供,用来配置和构建 RestTemplate 的构建器工具类。

    • 提供了多种便捷方法,如注册消息转换器方法、配置错误处理器方法、设置 URI 模板处理器方法等。
    • spring boot 已通过 RestTemplateAutoConfiguration 自动配置类将该类实例注册为 ioc Bean 实例,故可直接通过依赖注入来使用。
2.3 xxxForObject() 与 xxxForEntity()

  xxxForObject() 与 xxxForEntity() 区别如下:

xxxForObject()xxxForEntity()
返回值类型直接返回反序列化后的业务对象 T返回包装对象 ResponseENtity< T>
HTTP 元数据访问无法获取访问响应头/状态等信息可通过 getHeaders() 或 getStatusCode() 等方法获取
异常处理非 2xx 状态码直接抛出异常可先获取响应对象 ResponseENtity< T> 再手动校验状态码
适用场景仅需响应体的简单场景需要完整控制响应的复杂场景

3 使用姿势

3.1 ResponseCode 与 APIResponse
  • ResponseCode

    public enum ResponseCode {
    
        /**
         * base code
         */
        SUCCESS(1, "请求成功!"),
        FAILURE(-1, "请求失败!"),
        SYSTEM_ERROR(-1, "系统异常!"),
        PARAMS_ERROR(-1, "参数不能为空!"),
        DATA_DOES_NOT_EXIST(-1, "数据不存在!"),
    
        /**
         * login or register
         */
        ACCOUNT_OR_PASSWORD_ERROR(10100, "账号或密码错误!"),
        ACCOUNT_DOES_NOT_EXIST(10101, "账号不存在,请先注册!"),
        ;
    
        private final int code;
    
        private final String message;
    
        ResponseCode(int code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }
    
  • APIResponse

    @Data
    public class APIResponse<T> {
    
        private int code;
    
        private String message;
    
        private T data;
    
        public APIResponse() {
            this(ResponseCode.SUCCESS, null);
        }
    
        public APIResponse(int code, String message, T data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
    
        public APIResponse(ResponseCode responseCode, T data) {
            this.code = responseCode.getCode();
            this.message = responseCode.getMessage();
            this.data = data;
        }
    
        public static <T> APIResponse<T> success() {
            return success(ResponseCode.SUCCESS);
        }
    
        public static <T> APIResponse<T> success(T data) {
            return success(ResponseCode.SUCCESS, data);
        }
    
        public static <T> APIResponse<T> success(int code, String message) {
            return success(code, message, null);
        }
    
        public static <T> APIResponse<T> success(String message, T data) {
            return success(ResponseCode.SUCCESS.getCode(), message, data);
        }
    
        public static <T> APIResponse<T> success(int code, String message, T data) {
            return new APIResponse<>(code, message, data);
        }
    
        public static <T> APIResponse<T> success(ResponseCode responseCode) {
            return success(responseCode, null);
        }
    
        public static <T> APIResponse<T> success(ResponseCode responseCode, T data) {
            return new APIResponse<>(responseCode, data);
        }
    
        public static <T> APIResponse<T> failure() {
            return failure(ResponseCode.FAILURE.getMessage());
        }
    
        public static <T> APIResponse<T> failure(String message) {
            return failure(ResponseCode.FAILURE.getCode(), message);
        }
    
        public static <T> APIResponse<T> failure(int code, String message) {
            return failure(code, message, null);
        }
    
        public static <T> APIResponse<T> failure(int code, String message, T data) {
            return new APIResponse<>(code, message, data);
        }
    
        public static <T> APIResponse<T> failure(ResponseCode responseCode) {
            return failure(responseCode, null);
        }
    
        public static <T> APIResponse<T> failure(ResponseCode responseCode, T data) {
            return new APIResponse<>(responseCode, data);
        }
    }
    
3.2 GET
  • 示例一

    • 接口示例:

      @GetMapping("/one/{id}/{name}")
      public APIResponse<Object> one(@PathVariable("id") long id,
                                     @PathVariable("name") String name) {
          return APIResponse.success(id + name);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/one/{id}/{name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, params);
      
  • 示例二

    • 接口示例:

      @GetMapping("/two")
      public APIResponse<RequestVo> two(RequestVo requestVo) {
          return APIResponse.success(requestVo);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/two?id={id}&name={name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, params);
      
  • 示例三

    • 接口示例:

      @GetMapping("/three")
      public APIResponse<Object> three(@RequestParam("id") long id,
                                       @RequestParam("name") String name) {
          return APIResponse.success(id + name);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/three?id={id}&name={name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, params);
      
  • 示例四

    • 接口示例:

      @GetMapping("/four/{id}")
      public APIResponse<Object> four(@PathVariable("id") long id,
                                      @RequestParam("name") String name) {
          return APIResponse.success(id + name);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/four/{id}?name={name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.getForObject(url, APIResponse.class, params);
      
3.3 POST
  • 示例一

    • 接口示例:

      @PostMapping("/five/{id}/{name}")
      public APIResponse<Object> five(@PathVariable("id") long id,
                                      @PathVariable("name") String name) {
          return APIResponse.success(id + name);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/five/{id}/{name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, params);
      
  • 示例二

    • 接口示例:

      @PostMapping("/six")
      public APIResponse<RequestVo> six(RequestVo requestVo) {
          return APIResponse.success(requestVo);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/six?id={id}&name={name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, params);
      
  • 示例三

    • 接口示例:

      @PostMapping("/seven")
      public APIResponse<Object> seven(@RequestParam("id") long id,
                                       @RequestParam("name") String name) {
          return APIResponse.success(id + name);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/seven?id={id}&name={name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, params);
      
  • 示例四

    • 接口示例:

      @PostMapping("/eight/{id}")
      public APIResponse<Object> eight(@PathVariable("id") long id,
                                       @RequestParam("name") String name) {
          return APIResponse.success(id + name);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/eight/{id}?name={name}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, 1, "zed");
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.postForObject(url, null, APIResponse.class, params);
      
  • 示例五

    • 接口示例:

      @PostMapping("/nine")
      public APIResponse<Object> nine(@RequestBody RequestVo requestVo) {
          return APIResponse.success(requestVo);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/nine";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      Map<String, Object> params = new HashMap<>();
      params.put("id", 1);
      params.put("name", "Zed");
      APIResponse response = restTemplate.postForObject(url, params, APIResponse.class);
      
      // 方式二
      RequestVo requestVo = RequestVo.builder()
          .id(1L)
          .name("zed")
          .build();
      APIResponse response = restTemplate.postForObject(url, requestVo, APIResponse.class);
      
  • 示例六

    • 接口示例:

      @PostMapping("/ten/{id}")
      public APIResponse<Object> ten(@PathVariable("id") long id,
                                     @RequestBody RequestVo requestVo) {
          return APIResponse.success(requestVo.toString() + id);
      }
      
    • 调用示例:

      String url = "http://ip:port/test/test/ten/{id}";
      RestTemplate restTemplate = new RestTemplate();
      
      // 方式一
      RequestVo requestVo = RequestVo.builder()
          .id(1L)
          .name("zed")
          .build();
      APIResponse response = restTemplate.postForObject(url, requestVo, APIResponse.class, 2);
      
      // 方式二
      Map<String, Object> params = new HashMap<>();
      params.put("id", 11);
      
      RequestVo requestVo = RequestVo.builder()
          .id(1L)
          .name("zed")
          .build();
      APIResponse response = restTemplate.postForObject(url, requestVo, APIResponse.class, params);
      
3.4 exchange() 与 execute()

  exchange() 为高级封装方法,作用与 xxxForEntity() 类似,仅在参数表方面略有不同。execute() 为底层基础执行方法,较其它方法而言具有更大的灵活性。故,这俩不演示也罢!

4 RestTemplate 与 WebClient

  在 spring 5.0 版本以后,官方推荐使用 WebClient 作为替代,RestTemplate 将在未来版本中被标记为过时。当然,对于简单的同步请求场景,RestTemplate 仍是最佳选择。若在微服务环境中,则应考虑使用声明式的 Feign 客户端。

  RestTemplate 与 WebClient 区别如下:

RestTemplateWebClient
编程模型同步阻塞异步非阻塞
性能较低(阻塞)较高(基于 Reactor)
配置复杂度简单中等
功能特性基础 HTTP 操作流式处理、SSE 等高级特性
spring 版本spring 3.0+spring 5.0+
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红衣女妖仙

行行好,给点吃的吧!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值