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

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

InterceptingHttpAccessor


// @since 3.0
// @see InterceptingClientHttpRequestFactory
public abstract class InterceptingHttpAccessor extends HttpAccessor {
  // 装载需要作用在RestTemplate上的拦截器们~~~
  private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
  @Nullable
  private volatile ClientHttpRequestFactory interceptingRequestFactory;
  // 这里语意是set,所以是完全的替换掉(支持ordered排序哦~~~)
  public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
    if (this.interceptors != interceptors) {
      this.interceptors.clear();
      this.interceptors.addAll(interceptors);
      AnnotationAwareOrderComparator.sort(this.interceptors);
    }
  }
  // 复写了父类的这个方法很有意思
  // 意思为:若你调用者手动set进来了,那就以调用者设置的工厂为准 否则使用的是InterceptingClientHttpRequestFactory
  @Override
  public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
    super.setRequestFactory(requestFactory);
    this.interceptingRequestFactory = null;
  }
  // 若配置了拦截器,那么默认就使用InterceptingClientHttpRequestFactory,而不再是SimpleClientHttpRequestFactory了~~~
  @Override
  public ClientHttpRequestFactory getRequestFactory() {
    List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
    if (!CollectionUtils.isEmpty(interceptors)) {
      ClientHttpRequestFactory factory = this.interceptingRequestFactory;
      if (factory == null) {
        factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
        this.interceptingRequestFactory = factory;
      }
      return factory;
    } else {
      return super.getRequestFactory();
    }
  }
}


InterceptingHttpAccessor最主要的处理逻辑为:若发现调用者设置了请求拦截器,那么它创建的工厂是具有拦截功能的InterceptingClientHttpRequestFactory,否则就是默认的SimpleClientHttpRequestFactory。


InterceptingClientHttpRequestFactory工厂它产生的ClientHttpRequest是InterceptingClientHttpRequest,然而它就会执行拦截器的拦截方法喽:nextInterceptor.intercept(request, body, this)


提问:如有配置有多个请求拦截器,都会执行吗?

解答:这个千万不要犯迷糊和轻易下结论:以为没有迭代它(for循环)而只是iterator.next()就以为若有多个就只会执行一个,那就大错特错了。这里实际是形成了一个执行链条,只要拦截器的intercept方法内最终还调用执行器的intercept()方法,那么拦截器链就会一直执行下去。其根本缘由是第三个参数传入的是this,至始至终都是同一个执行器(this=InterceptingRequestExecution)


RestTemplate



RestTemplate采用同步方式执行 HTTP 请求的类,底层默认使用JDK原生 HttpURLConnection API。它实现了接口RestOperations,提供了非常多的模版方法(重载方法)让开发者能更简单地发送 HTTP 请求。


需要注意的是,RestTemplate是Spring 3.0就有了,但在Spring5.0后,Spring官方是推荐使用org.springframework.web.reactive.function.client.WebClient替代它,特别是对于异步的场景。


RestTemplate因为使用极其广泛,so即使到了Spring 5.0,官方只是建议替代,但并没有标注@Deprecated,因此至少目前你还可以想咋用就咋用吧。

但是AsyncRestTemplate是明确标注了@Deprecated,强烈建议使用org.springframework.web.reactive.function.client.WebClient去代替,所以在5.0后不建议再使用它了~。


当然还需要说明一点:若你的项目中没有使用到WebFlux的技术栈来处理请求,那么也没必要说为了使用而使用,所以没必要专门为了它而导包(个人建议)~

// @since 3.0
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
  // 去classpath探测  是否有这些消息转换器相关的jar~
  // 一般情况下我们都会导jackson2Present~~~
  private static boolean romePresent;
  private static final boolean jaxb2Present;
  private static final boolean jackson2Present;
  private static final boolean jackson2XmlPresent;
  private static final boolean jackson2SmilePresent;
  private static final boolean jackson2CborPresent;
  private static final boolean gsonPresent;
  private static final boolean jsonbPresent;
  ...
  // 下面四个变量很重要:
  // 消息转换器们(显然对JSON格式默认是支持得最好的)
  private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
  // 默认的请求异常处理器,Spring5.0后其实可以使用它ExtractingResponseErrorHandler
  // 它能够利用消息换换气提取你的错误内容。并且还支持自定义错误码、错误序列等等~
  private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
  // 用于URL的构建
  private UriTemplateHandler uriTemplateHandler;
  // 默认的返回值提取器~~~~
  private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
  // 空构造,应该是平时使用得最多的了:一切都使用默认的组件配置Resource等等
  public RestTemplate() {
    // 这个几个消息转换器是支持的。字节数组、字符串、
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    this.messageConverters.add(new StringHttpMessageConverter());
    this.messageConverters.add(new ResourceHttpMessageConverter(false));
    this.messageConverters.add(new SourceHttpMessageConverter<>());
    // 对form表单提交方式的支持
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    // 接下里便是一些列的判断,若类路径上有才会加进来
    if (jackson2Present) {
      this.messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    ...
    // new DefaultUriBuilderFactory()
    this.uriTemplateHandler = initUriTemplateHandler();
  }
  // 你懂的,若想用OkHttp,也可以在构造时就指定
  public RestTemplate(ClientHttpRequestFactory requestFactory) {
    this();
    setRequestFactory(requestFactory);
  }
  // 若不想用默认的消息转换器,也可以自己指定(其实一般都不这么去干,而是后面自己再add进来)
  public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
    Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
    this.messageConverters.addAll(messageConverters);
    this.uriTemplateHandler = initUriTemplateHandler();
  }
  ... // 省略上面属性的get/set犯法们
}


这部分源码我列出来,都是在对构建一个RestTemplate实例的准备工作相关方法,包括对各个相关组件的设置。


接下来更重要的便是它实现的接口方法了,我抽出一些关键点进行描述说明:


RestTemplate:
  @Override
  @Nullable
  public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
    //1、new AcceptHeaderRequestCallback(responseType)  它能在发送请求的之前这样一件事:
    // request.getHeaders().setAccept(allSupportedMediaTypes)
    RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    // 最终调用的是execute方法,此时URL是个字符串
    // responseExtractor返回值提取器使用的是消息转换器去读取body哒~
    // 返回值就是返回的body本身(不含有返回的响应头等等信息~)
    return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
  }
  // 它返回的是ResponseEntity,不会返回null的  最终调用的依旧是execute方法
  // 此时候用的就不是消息转换器的提取器了,而是内部类`ResponseEntityResponseExtractor`(底层还是依赖消息转换器)
  // 但是这个提取器,提取出来的可都是ResponseEntity<T>实例~
  @Override
  public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
    RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
    return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));
  }
  // HEAD请求:很简单,使用的提取器就是headersExtractor,从返回值里把响应header拿出来即可
  @Override
  public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException {
    return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables));
  }
  // POST请求
  @Override
  @Nullable
  public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
    // 1、HttpEntityRequestCallback  适配:把request适配成一个HttpEntity
    // 然后执行前,通过消息转换器把头信息、body信息等等都write进去
    RequestCallback requestCallback = httpEntityCallback(request);
    // 因为需要拿到URI,所以此处使用headersExtractor提取器先拿到响应的header即可~~~
    HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables);
    return (headers != null ? headers.getLocation() : null);
  }
  // 除了httpEntityCallback()不一样,其余和get请求一样
  @Override
  @Nullable
  public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {
    RequestCallback requestCallback = httpEntityCallback(request, responseType);
    HttpMessageConverterExtractor<T> responseExtractor =
        new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
  }
  // PUT请求:因为没有返回值,所以不需要返回值提取器。所以,非常的简单~~~
  @Override
  public void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException {
    RequestCallback requestCallback = httpEntityCallback(request);
    execute(url, HttpMethod.PUT, requestCallback, null, uriVariables);
  }
  // DELETE请求:也是木有返回值的。
  // 并且请注意:DELETE请求这里可都是不能接收body的,不能给请求设置请求体的
  // (虽然可能底层httpCLient支持,但这里不支持,请遵守规范)
  @Override
  public void delete(String url, Object... uriVariables) throws RestClientException {
    execute(url, HttpMethod.DELETE, null, null, uriVariables);
  }
  // OPTIONS请求:和HEAD请求的处理逻辑几乎一样
  @Override
  public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException {
    ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor();
    HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables);
    return (headers != null ? headers.getAllow() : Collections.emptySet());
  }
相关文章
|
9天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
20 0
|
1月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
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 IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
129 9
|
1月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
57 2
|
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 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
1月前
|
XML 前端开发 Java
拼多多1面:聊聊Spring MVC的工作原理!
本文详细剖析了Spring MVC的工作原理,涵盖其架构、工作流程及核心组件。Spring MVC采用MVC设计模式,通过DispatcherServlet、HandlerMapping、Controller和ViewResolver等组件高效处理Web请求。文章还探讨了DispatcherServlet的初始化和请求处理流程,以及HandlerMapping和Controller的角色。通过理解这些核心概念,开发者能更好地构建可维护、可扩展的Web应用。适合面试准备和技术深挖
43 0
|
1月前
|
负载均衡 Java API
Spring Cloud原理详解
Spring Cloud原理详解
73 0
|
1月前
|
负载均衡 Java 网络架构
Spring Cloud原理详解
介绍了Spring Cloud的原理和核心组件,包括服务注册与发现、配置管理、负载均衡、断路器、智能路由、分布式消息传递、分布式追踪和服务熔断等,旨在帮助开发人员快速构建和管理微服务架构中的分布式系统。
58 0
下一篇
无影云桌面