RestTemplate的使用和原理你都烂熟于胸了吗?【享学Spring MVC】(下)

简介: RestTemplate的使用和原理你都烂熟于胸了吗?【享学Spring MVC】(下)

所有方法大体执行逻辑一致,都是和RequestCallback、responseExtractor等有关,且最终都是委托给了最为底层的execute()方法去执行。


你是否疑问:它提供的put方法返回值都是void,若我put请求就有返回值肿么办呢?那么接下来就介绍更为通用的一个方法:exchange()


RestTemplate:
  @Override
  public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
    // 把请求体适配为HttpEntity
    RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
    // 消息提取器使用ResponseEntityResponseExtractor
    ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    // 从上两个部分就能看到:exchange方法的入参、出参都是非常通用的~~~
    return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
  }
  // ParameterizedTypeReference参数化类型,用于处理泛型
  // 上面的responseType就是个Class。这里是个参数化类型~~~~~
  @Override
  public <T> ResponseEntity<T> exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
    Type type = responseType.getType();
    RequestCallback requestCallback = httpEntityCallback(requestEntity, type);
    ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type);
    return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
  }
  // 这个方法就非常精简了,让调用者自己去构造RequestEntity,里面是包含了请求的URL和方法等信息的
  @Override
  public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) throws RestClientException {
    RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType);
    ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    return nonNull(doExecute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor));
  }


exchange所有方法使用的都是HttpEntity和ResponseEntity代表请求实体和响应实体,足以见到它设计的通用性。


在Spring3.2后提供了ParameterizedTypeReference来处理参数化类型—> 主要是为了处理List等的泛型


可以发现即使是exchange()方法,最终还是委托给execute/doExecute去执行的:

RestTemplate:
  // 3个execute方法。最终调用的都是doExecute方法
  // 它做的一件事:使用UriTemplateHandler把URL的参数填进去~~~
  // 底层使用的是我上文介绍的`UriComponentsBuilder`,还是比较简单的
  @Override
  @Nullable
  public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
    URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    return doExecute(expanded, method, requestCallback, responseExtractor);
  }
doExecute方法:
  @Nullable
  protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    ClientHttpResponse response = null;
    ClientHttpRequest request = createRequest(url, method);
    // 如果有回调,那就先回调处理一下子请求
    if (requestCallback != null) {
      requestCallback.doWithRequest(request);
    }
    // 真正意义上的发送请求。
    // 请注意:如果这里的request是`InterceptingClientHttpRequest`,那就回执行拦截器的intercept方法哦~~~
    // 至于什么时候是InterceptingClientHttpRequest呢?这个上面有讲的
    response = request.execute();
    // 处理结果(若有错误,那就抛出异常~~~)
    handleResponse(url, method, response);
    // 请求正常。那就使用返回值提取器responseExtractor提取出内容即可了~~~
    return (responseExtractor != null ? responseExtractor.extractData(response) : null);
    ...
    // 关闭响应(ClientHttpResponse继承了Closeable接口)
    finally {
      if (response != null) {
        response.close();
      }
    }
  }


看完doExecute()的模板式的实现步骤,就清楚了RestTemplate从发出一个请求到收到一个响应的完整过程。Spring设计了多个相关组件,提供钩子程序让我们可以干预到流程里面去,最常见的当然就是请求拦截器了,它在Ribbon负载均衡和Hystrix熔断器里面有很好的应用~


AsyncRestTemplate


它是@since 4.0新增的用于解决一些异步Http请求的场景,但它寿命比较短,在Spring5.0就标记为@Deprecated,而被推荐使用WebClient去代替它。


它的实现基础原理是:RestTemplate + SimpleAsyncTaskExecutor任务池的方式去实现的异步请求,返回值均为ListenableFuture。掌握了RestTemplate后,它使用起来是没有什么障碍的


极简使用Demo Show


看过了原理的描述,我有理由相信你已经烂熟于胸并对RestTemplate能够运用自如了。因此关于使用方面,本文只给如下非常简单的一个Demo Show我认为是够了的:

public static void main(String[] args) throws IOException {
    RestTemplate restTemplate = new RestTemplate();
    String pageHtml = restTemplate.getForObject("http://www.baidu.com", String.class);
    System.out.println(pageHtml); // 百度首页的html...
}


解释一点:这里请求得到的是一个html网页,所以HttpMessageConverterExtractor去提取响应时,使用的是StringHttpMessageConverter去处理的,提取代码如下


StringHttpMessageConverter:
  @Override
  protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
    // 从响应头的contentType里提取(若是application/json,那默认也是urf-8)
    // 若没有指定编码,就取值getDefaultCharset。比如本处访问百度,就取值默认值`ISO-8859-1`对body体进行编码的~
    Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
    return StreamUtils.copyToString(inputMessage.getBody(), charset);
  }


小伙伴把此请求案例可以和上面我使用ClientHttpRequestFactory发送请求的案例对比(或者和你自己使用HttpClient步骤对比),感受感受使用RestTemplate是多么的优雅~


推荐阅读


RestTemplate组件:ClientHttpRequestFactory、ClientHttpRequestInterceptor、ResponseExtractor【享学Spring MVC】

为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】


总结


微服务作为主流的今天,RestTemplate可谓是一把利器,每个程序员都应该掌握它。深入理解它对实际应用、调优都有很现实的意义,所以我相信本文能够帮助到你,做到烂熟于胸。

预告一下:下篇文章会原理分析告诉大家为何一个简单的@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
相关文章
|
5月前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
673 22
|
5月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1887 0
|
4月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
4月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
4月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
547 2
|
6月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
7月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
503 0
|
7月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
256 0
|
7月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
207 0
|
7月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
317 0