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战斗力翻倍)~~~



相关文章
|
1月前
|
SQL JavaScript Java
springboot+springm vc+mybatis实现增删改查案例!
springboot+springm vc+mybatis实现增删改查案例!
25 0
|
11天前
|
数据采集 前端开发 Java
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
23 3
|
11天前
|
存储 前端开发 Java
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
14 1
|
11天前
|
前端开发 Java Spring
数据之桥:深入Spring MVC中传递数据给视图的实用指南
数据之桥:深入Spring MVC中传递数据给视图的实用指南
29 3
|
20天前
|
前端开发 安全 Java
使用Java Web框架:Spring MVC的全面指南
【4月更文挑战第3天】Spring MVC是Spring框架的一部分,用于构建高效、模块化的Web应用。它基于MVC模式,支持多种视图技术。核心概念包括DispatcherServlet(前端控制器)、HandlerMapping(请求映射)、Controller(处理请求)、ViewResolver(视图解析)和ModelAndView(模型和视图容器)。开发流程涉及配置DispatcherServlet、定义Controller、创建View、处理数据、绑定模型和异常处理。
使用Java Web框架:Spring MVC的全面指南
|
27天前
|
敏捷开发 监控 前端开发
Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构
Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构
61 0
|
10月前
|
缓存 负载均衡 监控
Spring Cloud 五大组件 简介 Eureka、Ribbon、Hystrix、Feign和Zuul
Spring Cloud 五大组件 简介 Eureka、Ribbon、Hystrix、Feign和Zuul
1020 0
|
Java 容器 Spring
Spring 三大基础组件简介
    一,Bean,Core,Context关系   在Spring的各种组件中,Bean,Core,Context算是基础组件(ExpressionLanguage表达式支持, 这个主要就是用来支持一些spring XML配置文件表达式 和 注解中一些表达式解析,让配置有动态特性,spring早期的版本是没有这货的,不算是特别必须的, 特别核心的东西,只是为了灵活性加的),在Core container这一层构建起了整个Spring的骨骼架构。
1510 0
|
26天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
40 0
|
1月前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
111 0