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

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

前言


本文为深入了解Spring提供的Rest调用客户端RestTemplate开山,对它相关的一些组件做讲解。

Tips:请注意区分RestTemplateRedisTemplate哦~


ClientHttpRequestFactory


它是个函数式接口,用于根据URI和HttpMethod创建出一个ClientHttpRequest来发送请求~


ClientHttpRequest它代表请求的客户端,该接口继承自HttpRequest、HttpOutputMessage,只有一个ClientHttpResponse execute() throws IOException方法。其中Netty、HttpComponents、OkHttp3,HttpUrlConnection对它都有实现~


// @since 3.0  RestTemplate这个体系都是3.0后才有的
@FunctionalInterface
public interface ClientHttpRequestFactory { 
  // 返回一个ClientHttpRequest,这样调用其execute()方法就可以发送rest请求了~
  ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}


它的继承树如下:


image.png


可以直观的看到,我们可以使用Apache的HttpClient、OkHttp3、Netty4都可,但这些都需要额外导包,默认情况下Spring使用的是java.net.HttpURLConnection。


HttpClient最新版本:4.5.10

OkHttp最新版本:4.1.1(虽然版本号是4,但是GAV还是3哦:com.squareup.okhttp3)

Netty最新版本:4.1.39.Final(它的5版本可以宣告已死)


Spring4.0是新增了一个对异步支持的AsyncClientHttpRequestFactory(Spring5.0后标记为已废弃):


// 在Spring5.0后被标记为过时了,被org.springframework.http.client.reactive.ClientHttpConnector所取代(但还是可用的嘛)
@Deprecated
public interface AsyncClientHttpRequestFactory {
  // AsyncClientHttpRequest#executeAsync()返回的是ListenableFuture<ClientHttpResponse>
  // 可见它的异步是通过ListenableFuture实现的
  AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException;
}


使用工厂创建ClientHttpRequest,然后我们发请求就不用关心具体httpClient内部的细节了(可插拔使用二方库、三方库)

SimpleClientHttpRequestFactory


它是Spring内置默认的实现,使用的是JDK内置的java.net.URLConnection作为client客户端。


public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
  private static final int DEFAULT_CHUNK_SIZE = 4096;
  @Nullable
  private Proxy proxy; //java.net.Proxy
  private boolean bufferRequestBody = true; // 默认会缓冲body
  // URLConnection's connect timeout (in milliseconds).
  // 若值设置为0,表示永不超时 @see URLConnection#setConnectTimeout(int)
  private int connectTimeout = -1;
  // URLConnection#setReadTimeout(int) 
  // 超时规则同上
  private int readTimeout = -1;
  //Set if the underlying URLConnection can be set to 'output streaming' mode.
  private boolean outputStreaming = true;
  // 异步的时候需要
  @Nullable
  private AsyncListenableTaskExecutor taskExecutor;
  ... // 省略所有的set方法
  @Override
  public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    // 打开一个HttpURLConnection
    HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
    // 设置超时时间、请求方法等一些参数到connection
    prepareConnection(connection, httpMethod.name());
    //SimpleBufferingClientHttpRequest的excute方法最终使用的是connection.connect();
    // 然后从connection中得到响应码、响应体~~~
    if (this.bufferRequestBody) {
      return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
    } else {
      return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
    }
  }
  // createAsyncRequest()方法略,无非就是在线程池里异步完成请求
  ...
}


需要注意的是:JDK <1.8 doesn't support getOutputStream with HTTP DELETE,也就是说如果JDK的版本低于1.8的话,那么Delete请求是不支持body体的。


Demo Show:

public static void main(String[] args) throws IOException {
    SimpleClientHttpRequestFactory clientFactory  = new SimpleClientHttpRequestFactory();
  // ConnectTimeout只有在网络正常的情况下才有效,因此两个一般都设置
    clientFactory.setConnectTimeout(5000); //建立连接的超时时间  5秒
    clientFactory.setReadTimeout(5000); // 传递数据的超时时间(在网络抖动的情况下,这个参数很有用)
    ClientHttpRequest client = clientFactory.createRequest(URI.create("https://www.baidu.com"), HttpMethod.GET);
    // 发送请求
    ClientHttpResponse response = client.execute();
    System.out.println(response.getStatusCode()); //200 OK
    System.out.println(response.getStatusText()); // OK
    System.out.println(response.getHeaders()); //
    // 返回内容 是个InputStream
    byte[] bytes = FileCopyUtils.copyToByteArray(response.getBody());
    System.out.println(new String(bytes, StandardCharsets.UTF_8)); // 百度首页内容的html
}


关于HttpURLConnection的API使用,需注意如下几点:


  1. HttpURLConnection对象不能直接构造,需要通过URL类中的openConnection()方法来获得
  2. HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的TCP连接,并没有实际发送HTTP请求。HTTP请求实际上直到我们获取服务器响应数据(如调用getInputStream()、getResponseCode()等方法)时才正式发送出去1. 配置信息都需要在connect()方法执行之前完成
  3. HttpURLConnection是基于HTTP协议的,其底层通过socket通信实现。如果不设置超时(timeout),在网络异常的情况下,可能会导致程序僵死而不继续往下执行。请务必100%设置
  4. HTTP正文的内容是通过OutputStream流写入的, 向流中写入的数据不会立即发送到网络,而是存在于内存缓冲区中,待流关闭时,根据写入的内容生成HTTP正文
  5. 调用getInputStream()方法时,返回一个输入流,用于从中读取服务器对于HTTP请求的返回信息。
  6. HttpURLConnection.connect()不是必须的。当我们需要返回值时,比如我们使用HttpURLConnection.getInputStream()方法的时候它就会自动发送请求了,所以完全没有必要调用connect()方法了(没必要先建立Tcp嘛~)。


使用哪一个底层http库?


我们知道HttpURLConnection它在功能上是有些不足的(简单的提交参数可以满足)。绝大部分情况下Web站点的网页可能没这么简单,这些页面并不是通过一个简单的URL就可访问的,可能需要用户登录而且具有相应的权限才可访问该页面。在这种情况下,就需要涉及Session、Cookie的处理了,如果打算使用HttpURLConnection来处理这些细节,当然也是可能实现的,只是处理起来难度就大了。


这个时候,Apache开源组织提供了一个HttpClient项目,可以用于发送HTTP请求,接收HTTP响应(包含HttpGet、HttpPost…等各种发送请求的对象)。


它不会缓存服务器的响应,不能执行HTML页面中嵌入的Javascript代码;也不会对页面内容进行任何解析、处理


因此,下面我就让Spring使用HttpClient为示例演示使用三方库:

1、导包

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.10</version>
</dependency>


Tips:Requires Apache HttpComponents 4.3 or higher, as of Spring 4.0.


2、案例使用


案例内容仅仅只需把上例第一句话换成使用HttpComponentsClientHttpRequestFactory它的实例,其余都不用变化即可成功看到效果。可以看看这个类它具体做了什么


// @since 3.1 3.1后出现的。
public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
  private HttpClient httpClient;
  @Nullable
  private RequestConfig requestConfig; // 这个配置就是可以配置超时等等乱七八糟client属性的类
  private boolean bufferRequestBody = true;
  //=========下面是构造函数们=========
  public HttpComponentsClientHttpRequestFactory() {
    // HttpClientBuilder.create().useSystemProperties().build();
    // 所有若是这里,配置超时时间可以这么来设置也可:
    // System.setProperty(”sun.net.client.defaultConnectTimeout”, “5000″);
    this.httpClient = HttpClients.createSystem();
  }
  // 当然可以把你配置好了的Client扔进来
  public HttpComponentsClientHttpRequestFactory(HttpClient httpClient) {
    this.httpClient = httpClient;
  }
  ... // 省略设置超时时间。。。等等属性的一些get/set
  // 超时信息啥的都是保存在`RequestConfig`里的
  @Override
  public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    HttpClient client = getHttpClient(); // 拿到你指定的client)=(或者系统缺省的)
    // switch语句逻辑:HttpMethod == GET --> HttpGet HEAD --> HttpHead ...
    HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
    postProcessHttpRequest(httpRequest);
    ...
  }
}


实际使用的是HttpClient完成的请求。另外OkHttp3ClientHttpRequestFactory使用的是okhttp3.OkHttpClient发送请求;Netty4ClientHttpRequestFactory使用的是io.netty.channel.EventLoopGroup。此处就不一一例举了


Spring5.0以后,Netty4ClientHttpRequestFactory过期了,建议使用org.springframework.http.client.reactive.ReactorClientHttpConnector代替~

关于HttpURLConnection、HttpClient、OkHttpClient的简单比较:


  • HttpURLConnection:

- 优点:JDK内置支持,java的标准类

- 缺点:API不够友好,什么都没封装,用起来太原始,不方便(这其实有时候也算优点,原始就证明好控~)


  • HttpClient:

- 优点:功能强大,API友好,使用率够高,几乎成为了实际意义上的标准(相当于对

HttpURLConnection的封装)

- 缺点:性能稍低(比HttpURLConnection低,但4.3后使用连接池进行了改善),API较臃肿,其实Android已经弃用了它~


  • OkHttpClient:新一代的Http访问客户端

- 优点:一个专注于性能和易用性的HTTP客户端(节约宽带,Android推荐使用),它设计的首要目标就是高效。提供了最新的 HTTP 协议版本 HTTP/2 和 SPDY 的支持。如果 HTTP/2 和 SPDY 不可用,OkHttp 会使用连接池来复用连接以提高效率

- 暂无。


image.png


关于Apache HttpClient,Android5.0之后已经废弃使用它了(API太多,太重),推荐使用更轻量的HttpUrlConnection。(Java开发还是推荐用HttpClient)


OkHttp优点较多:支持SPDY,可以合并多个到同一个主机的请求;OkHttp实现的诸多技术如:连接池,gziping,缓存等;OkHttp 处理了很多网络疑难杂症:会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP;OkHttp是一个Java的HTTP+SPDY客户端开发包,同时也支持Android。默认情况下,OKHttp会自动处理常见的网络问题,像二次连接、SSL的握手问题。支持文件上传、下载、cookie、session、https证书等几乎所有功能。支持取消某个请求


综上所述,不管是Java还是Android,我推荐的自然都是OkHttp(OkHttp使用Okio进行数据传输。都是Square公司自家的,Square公司还出了一个Retrofit库配合OkHttp战斗力翻倍)~~~



相关文章
|
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
【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
下一篇
无影云桌面