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

简介: RestTemplate组件:ClientHttpRequestFactory、ClientHttpRequestInterceptor、ResponseExtractor【享学Spring MVC】(下)

ResponseExtractor


响应提取器:从Response中提取数据。RestTemplate请求完成后,都是通过它来从ClientHttpResponse提取出指定内容(比如请求头、请求Body体等)~


image.png


它的直接实现似乎只有HttpMessageConverterExtractor,当然它也是最为重要的一个实现,和HttpMessageConverter相关。

在解释它之前,先看看这个:MessageBodyClientHttpResponseWrapper,它的特点:它不仅可以通过实际读取输入流来检查响应是否有消息体,还可以检查其长度是否为0(即空)


// @since 4.1.5  它是一个访问权限是default的类,是对其它ClientHttpResponse的一个包装
class MessageBodyClientHttpResponseWrapper implements ClientHttpResponse {
  private final ClientHttpResponse response;
  // java.io.PushbackInputStream
  @Nullable
  private PushbackInputStream pushbackInputStream;
  // 判断相应里是否有body体
  // 若响应码是1xx 或者是204;或者getHeaders().getContentLength() == 0 那就返回false  否则返回true
  public boolean hasMessageBody() throws IOException {
    HttpStatus status = HttpStatus.resolve(getRawStatusCode());
    if (status != null && (status.is1xxInformational() || status == HttpStatus.NO_CONTENT || status == HttpStatus.NOT_MODIFIED)) {
      return false;
    }
    if (getHeaders().getContentLength() == 0) {
      return false;
    }
    return true;
  }
  // 上面是完全格局状态码(ContentLength)来判断是否有body体的~~~这里会根据流来判断
  // 如果response.getBody() == null,返回true
  // 若流里有内容,最终就用new PushbackInputStream(body)包装起来~~~
  public boolean hasEmptyMessageBody() throws IOException {
    ...
  }
  ...  // 其余接口方法都委托~
  @Override
  public InputStream getBody() throws IOException {
    return (this.pushbackInputStream != null ? this.pushbackInputStream : this.response.getBody());
  }
}


它的作用就是包装后,提供两个方法hasMessageBody、hasEmptyMessageBody方便了对body体内容进行判断


// @since 3.0 泛型T:the data type
public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
  // java.lang.reflect.Type
  private final Type responseType;
  // 这个泛型也是T,表示数据的Class嘛~
  // 该calss有可能就是上面的responseType
  @Nullable
  private final Class<T> responseClass;
  // 重要:用于消息解析的转换器
  private final List<HttpMessageConverter<?>> messageConverters;
  ... // 省略构造函数
  // 从ClientHttpResponse 里提取值
  @Override
  @SuppressWarnings({"unchecked", "rawtypes", "resource"})
  public T extractData(ClientHttpResponse response) throws IOException {
    MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
    // 若没有消息体(状态码不对 或者 消息体为空都被认为是木有)
    if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
      return null;
    }
    // content-type若响应头header里没有指定,那默认是它MediaType.APPLICATION_OCTET_STREAM
    MediaType contentType = getContentType(responseWrapper);
    // 遍历所有的messageConverters,根据contentType 来选则一个消息转换器
    // 最终return messageConverter.read((Class) this.responseClass, responseWrapper)
    ...
  }
}


它的处理逻辑理解起来非常简单:利用contentType找到一个消息转换器,最终HttpMessageConverter.read()把消息读出来转换成Java对象。

它还有两个内部类的实现如下(都是RestTemplate的私有内部类):



RestTemplate:
  // 提取为`ResponseEntity`  最终委托给HttpMessageConverterExtractor完成的
  private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> {
    @Nullable
    private final HttpMessageConverterExtractor<T> delegate;
    public ResponseEntityResponseExtractor(@Nullable Type responseType) {
      // 显然:只有请求的返回值不为null 才有意义~
      if (responseType != null && Void.class != responseType) {
        this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
      } else {
        this.delegate = null;
      }
    }
    // 数据提取。都是交给`delegate.extractData(response)`做了,然后new一个ResponseEntity出来包装进去
    // 若木有返回值(delegate=null),那就是一个`ResponseEntity`实例,body为null
    @Override
    public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException {
      if (this.delegate != null) {
        T body = this.delegate.extractData(response);
        return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body);
      }
      else {
        return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build();
      }
    }
  }
  // 提取请求头
  private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> {
    @Override
    public HttpHeaders extractData(ClientHttpResponse response) {
      return response.getHeaders();
    }
  }


UriTemplateHandler

这个组件它用于定义用变量扩展uri模板的方法


// @since 4.2 出现较晚  
// @see RestTemplate#setUriTemplateHandler(UriTemplateHandler)
public interface UriTemplateHandler {
  URI expand(String uriTemplate, Map<String, ?> uriVariables);
  URI expand(String uriTemplate, Object... uriVariables);
}


关于URI的处理,最终都是委托给UriComponentsBuilder来完成。若对这块还存在一定疑问的,强烈强烈强烈 参考这里


推荐阅读


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


总结


本文介绍的组件是去理解RestTemplate必备的组件们,属于开山篇。因为RestTemplate使用频繁,并且经常需要调优,因此我寄希望大家也能对它做较为深入的了解,这也是我写本系列的目的,共勉。

相关文章
|
15天前
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
37 5
|
1月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
57 2
|
2月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
1月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
126 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
2月前
|
XML 缓存 Java
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
49 10
|
2月前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
2月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
3月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
3月前
|
人工智能 自然语言处理 Java
Spring AI,Spring团队开发的新组件,Java工程师快来一起体验吧
文章介绍了Spring AI,这是Spring团队开发的新组件,旨在为Java开发者提供易于集成的人工智能API,包括机器学习、自然语言处理和图像识别等功能,并通过实际代码示例展示了如何快速集成和使用这些AI技术。
Spring AI,Spring团队开发的新组件,Java工程师快来一起体验吧
下一篇
无影云桌面