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