2023-09-12  阅读(3)
原文作者:一直不懂 原文地址: https://blog.csdn.net/shenchaohao12321/article/details/88089363

同步客户端执行HTTP请求,在底层HTTP客户端库(如JDK HttpURLConnection,Apache HttpComponents等)上公开简单的模板方法API。RestTemplate除了支持频率较低的情况的通用交换和执行方法之外,还通过HTTP方法提供常见方案的模板。
注意 :从5.0开始,非阻塞,反应式org.springframework.web.reactive.client.WebClient提供了RestTemplate的现代替代方案,同时有效支持同步和异步以及流方案。 RestTemplate将在未来版本中弃用,并且不会在未来添加主要的新功能。

1、构造函数

    public RestTemplate() {
       this.messageConverters.add(new ByteArrayHttpMessageConverter());
       this.messageConverters.add(new StringHttpMessageConverter());
       this.messageConverters.add(new ResourceHttpMessageConverter(false));
       try {
          this.messageConverters.add(new SourceHttpMessageConverter<>());
       }
       catch (Error err) {
          // Ignore when no TransformerFactory implementation is available
       }
       this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    
       if (romePresent) {
          this.messageConverters.add(new AtomFeedHttpMessageConverter());
          this.messageConverters.add(new RssChannelHttpMessageConverter());
       }
    
       if (jackson2XmlPresent) {
          this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
       }
       else if (jaxb2Present) {
          this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
       }
    
       if (jackson2Present) {
          this.messageConverters.add(new MappingJackson2HttpMessageConverter());
       }
       else if (gsonPresent) {
          this.messageConverters.add(new GsonHttpMessageConverter());
       }
       else if (jsonbPresent) {
          this.messageConverters.add(new JsonbHttpMessageConverter());
       }
    
       if (jackson2SmilePresent) {
          this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
       }
       if (jackson2CborPresent) {
          this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
       }
    
       this.uriTemplateHandler = initUriTemplateHandler();
    }

1.1、初始化HttpMessageConverter

策略接口,代表一个可以转换HTTP请求和响应的转换器。

    public interface HttpMessageConverter<T> {
       //指示此转换器是否可以读取给定的类。
       //clazz  - 测试可读性的类
       //mediaType  - 要读取的媒体类型(如果未指定,则可以为null); 通常是Content-Type标头的值。
       boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
       //指示此转换器是否可以写入给定的类。
       boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
       //返回此转换器支持的MediaType对象列表。
       List<MediaType> getSupportedMediaTypes();
       //从给定的输入消息中读取给定类型的对象,并返回它。
       T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
             throws IOException, HttpMessageNotReadableException;
       //将给定对象写入给定的输出消息。
       void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
             throws IOException, HttpMessageNotWritableException;
    }

在默认构造函数会初始化很多HttpMessageConverter实例,这些实例会在三处地方起到作用:

一是这些实例被封装在AcceptHeaderRequestCallback中,当执行http请求时会根据responseType来选取canRead()方法支持的HttpMessageConverter,通过getSupportedMediaTypes()方法返回的MediaType设置http头Accept。

    private class AcceptHeaderRequestCallback implements RequestCallback {
       @Nullable
       private final Type responseType;
       public AcceptHeaderRequestCallback(@Nullable Type responseType) {
          this.responseType = responseType;
       }
       @Override
       public void doWithRequest(ClientHttpRequest request) throws IOException {
          if (this.responseType != null) {
             List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
                   .filter(converter -> canReadResponse(this.responseType, converter))
                   .flatMap(this::getSupportedMediaTypes)
                   .distinct()
                   .sorted(MediaType.SPECIFICITY_COMPARATOR)
                   .collect(Collectors.toList());
             if (logger.isDebugEnabled()) {
                logger.debug("Accept=" + allSupportedMediaTypes);
             }
             request.getHeaders().setAccept(allSupportedMediaTypes);
          }
       }
       private boolean canReadResponse(Type responseType, HttpMessageConverter<?> converter) {
          Class<?> responseClass = (responseType instanceof Class ? (Class<?>) responseType : null);
          if (responseClass != null) {
             return converter.canRead(responseClass, null);
          }
          else if (converter instanceof GenericHttpMessageConverter) {
             GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
             return genericConverter.canRead(responseType, null, null);
          }
          return false;
       }
       private Stream<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) {
          return messageConverter.getSupportedMediaTypes()
                .stream()
                .map(mediaType -> {
                   if (mediaType.getCharset() != null) {
                      return new MediaType(mediaType.getType(), mediaType.getSubtype());
                   }
                   return mediaType;
                });
       }
    }

二是当请求时POST/PUT操作时,会使用HttpEntityRequestCallback这种AcceptHeaderRequestCallback,他除了可以设置http头Accept(通过继承的方式)外,还会将请求体写入ClientHttpRequest中,写入流程是从构造方法实例化的HttpMessageConverter中选出canWrite()方法支持的实例,然后调用write()方法写入。

    private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback {
       private final HttpEntity<?> requestEntity;
       public HttpEntityRequestCallback(@Nullable Object requestBody) {
          this(requestBody, null);
       }
       public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) {
          super(responseType);
          if (requestBody instanceof HttpEntity) {
             this.requestEntity = (HttpEntity<?>) requestBody;
          }
          else if (requestBody != null) {
             this.requestEntity = new HttpEntity<>(requestBody);
          }
          else {
             this.requestEntity = HttpEntity.EMPTY;
          }
       }
       @Override
       @SuppressWarnings("unchecked")
       public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
          super.doWithRequest(httpRequest);
          Object requestBody = this.requestEntity.getBody();
          if (requestBody == null) {
             HttpHeaders httpHeaders = httpRequest.getHeaders();
             HttpHeaders requestHeaders = this.requestEntity.getHeaders();
             if (!requestHeaders.isEmpty()) {
                requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
             }
             if (httpHeaders.getContentLength() < 0) {
                httpHeaders.setContentLength(0L);
             }
          }
          else {
             Class<?> requestBodyClass = requestBody.getClass();
             Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
                   ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
             HttpHeaders httpHeaders = httpRequest.getHeaders();
             HttpHeaders requestHeaders = this.requestEntity.getHeaders();
             MediaType requestContentType = requestHeaders.getContentType();
             for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
                if (messageConverter instanceof GenericHttpMessageConverter) {
                   GenericHttpMessageConverter<Object> genericConverter =
                         (GenericHttpMessageConverter<Object>) messageConverter;
                   if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
                      if (!requestHeaders.isEmpty()) {
                         requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
                      }
                      logBody(requestBody, requestContentType, genericConverter);
                      genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
                      return;
                   }
                }
                else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
                   if (!requestHeaders.isEmpty()) {
                      requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
                   }
                   logBody(requestBody, requestContentType, messageConverter);
                   ((HttpMessageConverter<Object>) messageConverter).write(
                         requestBody, requestContentType, httpRequest);
                   return;
                }
             }
             String message = "No HttpMessageConverter for [" + requestBodyClass.getName() + "]";
             if (requestContentType != null) {
                message += " and content type [" + requestContentType + "]";
             }
             throw new RestClientException(message);
          }
       }
       private void logBody(Object body, @Nullable MediaType mediaType, HttpMessageConverter<?> converter) {
          if (logger.isDebugEnabled()) {
             if (mediaType != null) {
                logger.debug("Writing [" + body + "] as \"" + mediaType + "\"");
             }
             else {
                String classname = converter.getClass().getName();
                logger.debug("Writing [" + body + "] with " + classname);
             }
          }
       }
    }

三是这些实例被封装在HttpMessageConverterExtractor中在发送http请求后用来解析http响应消息为相应的实体类。

    //响应提取器使用给定的实体转换器将响应转换为类型T.
    public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> {
       private final Type responseType;
       @Nullable
       private final Class<T> responseClass;
       private final List<HttpMessageConverter<?>> messageConverters;
       private final Log logger;
       //使用给定的响应类型和消息转换器创建HttpMessageConverterExtractor的新实例。 给定的转换器必须支持响应类型。
       public HttpMessageConverterExtractor(Class<T> responseType, List<HttpMessageConverter<?>> messageConverters) {
          this((Type) responseType, messageConverters);
       }
       public HttpMessageConverterExtractor(Type responseType, List<HttpMessageConverter<?>> messageConverters) {
          this(responseType, messageConverters, LogFactory.getLog(HttpMessageConverterExtractor.class));
       }
    
       @SuppressWarnings("unchecked")
       HttpMessageConverterExtractor(Type responseType, List<HttpMessageConverter<?>> messageConverters, Log logger) {
          Assert.notNull(responseType, "'responseType' must not be null");
          Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
          this.responseType = responseType;
          this.responseClass = (responseType instanceof Class ? (Class<T>) responseType : null);
          this.messageConverters = messageConverters;
          this.logger = logger;
       }
    
    
       @Override
       @SuppressWarnings({"unchecked", "rawtypes", "resource"})
       public T extractData(ClientHttpResponse response) throws IOException {
          MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
          if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
             return null;
          }
          MediaType contentType = getContentType(responseWrapper);
    
          try {
             for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
                if (messageConverter instanceof GenericHttpMessageConverter) {
                   GenericHttpMessageConverter<?> genericMessageConverter =
                         (GenericHttpMessageConverter<?>) messageConverter;
                   if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
                      if (logger.isDebugEnabled()) {
                         ResolvableType resolvableType = ResolvableType.forType(this.responseType);
                         logger.debug("Reading to [" + resolvableType + "]");
                      }
                      return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
                   }
                }
                if (this.responseClass != null) {
                   if (messageConverter.canRead(this.responseClass, contentType)) {
                      if (logger.isDebugEnabled()) {
                         String className = this.responseClass.getName();
                         logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
                      }
                      return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
                   }
                }
             }
          }
          catch (IOException | HttpMessageNotReadableException ex) {
             throw new RestClientException("Error while extracting response for type [" +
                   this.responseType + "] and content type [" + contentType + "]", ex);
          }
    
          throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " +
                "for response type [" + this.responseType + "] and content type [" + contentType + "]");
       }
    
       //根据“Content-Type”标头确定响应的Content-Type,否则默认为MediaType.APPLICATION_OCTET_STREAM。
       @Nullable
       protected MediaType getContentType(ClientHttpResponse response) {
          MediaType contentType = response.getHeaders().getContentType();
          if (contentType == null) {
             if (logger.isTraceEnabled()) {
                logger.trace("No content-type, using 'application/octet-stream'");
             }
             contentType = MediaType.APPLICATION_OCTET_STREAM;
          }
          return contentType;
       }
    
    }

1.2、初始化UriTemplateHandler

初始化一个URI_COMPONENT编码格式的DefaultUriBuilderFactory。

    private static DefaultUriBuilderFactory initUriTemplateHandler() {
       DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
       uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT);  // for backwards compatibility..
       return uriFactory;
    }

202309122024134411.png

    //定义使用变量扩展URI模板的方法。
    public interface UriTemplateHandler {
       //使用URI变量数组展开给定的URI模板。
       URI expand(String uriTemplate, Map<String, ?> uriVariables);
       URI expand(String uriTemplate, Object... uriVariables);
    }
    public interface UriBuilderFactory extends UriTemplateHandler {
       //使用给定的URI模板初始化构建器。
       UriBuilder uriString(String uriTemplate);
       //使用默认设置创建URI构建器。
       UriBuilder builder();
    
    }

在发起一个http请求求会先使用这个UriTemplateHandler将模板url和uri变量解析成一个URI然后交给doExecute()方法发起请求。

    @Override
    @Nullable
    public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
          @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
       URI expanded = getUriTemplateHandler().expand(url, uriVariables);
       return doExecute(expanded, method, requestCallback, responseExtractor);
    }

下面是DefaultUriBuilderFactory#expand(java.lang.String, java.util.Map<java.lang.String,?>)方法实现:

    public URI expand(String uriTemplate, Map<String, ?> uriVars) {
       return uriString(uriTemplate).build(uriVars);
    }
    public UriBuilder uriString(String uriTemplate) {
       return new DefaultUriBuilder(uriTemplate);
    }

URI的创建使用DefaultUriBuilder的build()方法。

    @Override
    public URI build(Map<String, ?> uriVars) {
       if (!defaultUriVariables.isEmpty()) {
          Map<String, Object> map = new HashMap<>();
          map.putAll(defaultUriVariables);
          map.putAll(uriVars);
          uriVars = map;
       }
       if (encodingMode.equals(EncodingMode.VALUES_ONLY)) {
          uriVars = UriUtils.encodeUriVariables(uriVars);
       }
       UriComponents uric = this.uriComponentsBuilder.build().expand(uriVars);
       return createUri(uric);
    }
    
    private URI createUri(UriComponents uric) {
       if (encodingMode.equals(EncodingMode.URI_COMPONENT)) {
          uric = uric.encode();
       }
       return URI.create(uric.toString());
    }

从代码上面代码可以看出,URI的创建又是使用了uriComponentsBuilder.build().expand(uriVars);这个uriComponentsBuilder是在DefaultUriBuilder构造方法完成初始化的,如下:

    private class DefaultUriBuilder implements UriBuilder {
       private final UriComponentsBuilder uriComponentsBuilder;
       public DefaultUriBuilder(String uriTemplate) {
          this.uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
       }
       private UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) {
          UriComponentsBuilder result;
          if (StringUtils.isEmpty(uriTemplate)) {
             result = baseUri != null ? baseUri.cloneBuilder() : UriComponentsBuilder.newInstance();
          }
          else if (baseUri != null) {
             UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate);
             UriComponents uri = builder.build();
             result = uri.getHost() == null ? baseUri.cloneBuilder().uriComponents(uri) : builder;
          }
          else {
             //使用正则分解出uriTemplate各个部分赋给UriComponentsBuilder对应的字段
             result = UriComponentsBuilder.fromUriString(uriTemplate);
          }
          if (encodingMode.equals(EncodingMode.TEMPLATE_AND_VALUES)) {
             result.encode();
          }
          parsePathIfNecessary(result);
          return result;
       }
    
       private void parsePathIfNecessary(UriComponentsBuilder result) {
          //EncodingMode.URI_COMPONENT在RestTemplate中是默认编码模式
          //如果存在scheme-specific-part 从中分离path
          if (parsePath && encodingMode.equals(EncodingMode.URI_COMPONENT)) {
             UriComponents uric = result.build();
             String path = uric.getPath();
             result.replacePath(null);
             for (String segment : uric.getPathSegments()) {
                result.pathSegment(segment);
             }
             if (path != null && path.endsWith("/")) {
                result.path("/");
             }
          }
       }
       ...
    }

关于URI各部分含义请参考Uri详解之——Uri结构与代码提取URI介绍

build()方法返回一个UriComponents实例,表示URI组件的不可变集合,将组件类型映射到String值。

    public UriComponents build() {
       return build(false);
    }
    public UriComponents build(boolean encoded) {
       return buildInternal(encoded ?
             EncodingHint.FULLY_ENCODED :
             this.encodeTemplate ? EncodingHint.ENCODE_TEMPLATE : EncodingHint.NONE);
    }
    private UriComponents buildInternal(EncodingHint hint) {
       UriComponents result;
       if (this.ssp != null) {
          //带有scheme-specific-part部分的使用OpaqueUriComponents
          result = new OpaqueUriComponents(this.scheme, this.ssp, this.fragment);
       }
       else {
          HierarchicalUriComponents uric = new HierarchicalUriComponents(this.scheme, this.fragment,
                this.userInfo, this.host, this.port, this.pathBuilder.build(), this.queryParams,
                hint == EncodingHint.FULLY_ENCODED);
    
          result = hint == EncodingHint.ENCODE_TEMPLATE ? uric.encodeTemplate(this.charset) : uric;
       }
       //默认为空
       if (!this.uriVariables.isEmpty()) {
          result = result.expand(name -> this.uriVariables.getOrDefault(name, UriTemplateVariables.SKIP_VALUE));
       }
       return result;
    }

UriComponents#expand(java.util.Map<java.lang.String,?>)方法使用给定映射中的值替换所有URI模板变量。

    public final UriComponents expand(Map<String, ?> uriVariables) {
       Assert.notNull(uriVariables, "'uriVariables' must not be null");
       return expandInternal(new MapTemplateVariables(uriVariables));
    }

expandInternal()方法是抽象方法由子类实现,它的参数是一个UriTemplateVariables,内部会调用它getValue()方法获取模板参数值。对于Map<String, ?>类型模板参数值会将其封装成MapTemplateVariables,getValue()方法会根据map的key获取参数值,所以map类型模板参数变量的顺序不重要。对于可变参数类型的模板参数值会将其封装成VarArgsTemplateVariables,getValue()方法是根据可变参数值的顺序返回参数值,所以模板参数变量的顺序应该与可变参数一致,并且变量名称不重要。

    private static class MapTemplateVariables implements UriTemplateVariables {
       private final Map<String, ?> uriVariables;
       public MapTemplateVariables(Map<String, ?> uriVariables) {
          this.uriVariables = uriVariables;
       }
       @Override
       @Nullable
       public Object getValue(@Nullable String name) {
          if (!this.uriVariables.containsKey(name)) {
             throw new IllegalArgumentException("Map has no value for '" + name + "'");
          }
          return this.uriVariables.get(name);
       }
    }
    private static class VarArgsTemplateVariables implements UriTemplateVariables {
       private final Iterator<Object> valueIterator;
       public VarArgsTemplateVariables(Object... uriVariableValues) {
          this.valueIterator = Arrays.asList(uriVariableValues).iterator();
       }
       @Override
       @Nullable
       public Object getValue(@Nullable String name) {
          if (!this.valueIterator.hasNext()) {
             throw new IllegalArgumentException("Not enough variable values available to expand '" + name + "'");
          }
          return this.valueIterator.next();
       }
    }

HierarchicalUriComponents#expandInternal()方法。

    @Override
    protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVariables) {
    
       Assert.state(!this.encodeState.equals(EncodeState.FULLY_ENCODED),
             "URI components already encoded, and could not possibly contain '{' or '}'.");
    
       String schemeTo = expandUriComponent(getScheme(), uriVariables, this.variableEncoder);
       String fragmentTo = expandUriComponent(getFragment(), uriVariables, this.variableEncoder);
       String userInfoTo = expandUriComponent(this.userInfo, uriVariables, this.variableEncoder);
       String hostTo = expandUriComponent(this.host, uriVariables, this.variableEncoder);
       String portTo = expandUriComponent(this.port, uriVariables, this.variableEncoder);
       PathComponent pathTo = this.path.expand(uriVariables, this.variableEncoder);
       MultiValueMap<String, String> queryParamsTo = expandQueryParams(uriVariables);
    
       return new HierarchicalUriComponents(schemeTo, fragmentTo, userInfoTo,
             hostTo, portTo, pathTo, queryParamsTo, this.encodeState, this.variableEncoder);
    }
    
    private MultiValueMap<String, String> expandQueryParams(UriTemplateVariables variables) {
       int size = this.queryParams.size();
       MultiValueMap<String, String> result = new LinkedMultiValueMap<>(size);
       UriTemplateVariables queryVariables = new QueryUriTemplateVariables(variables);
       this.queryParams.forEach((key, values) -> {
          String name = expandUriComponent(key, queryVariables, this.variableEncoder);
          List<String> expandedValues = new ArrayList<>(values.size());
          for (String value : values) {
             expandedValues.add(expandUriComponent(value, queryVariables, this.variableEncoder));
          }
          result.put(name, expandedValues);
       });
       return CollectionUtils.unmodifiableMultiValueMap(result);
    }

expandUriComponent()方法用于使用参数值替换模板变量。

    @Nullable
    static String expandUriComponent(@Nullable String source, UriTemplateVariables uriVariables,
          @Nullable UnaryOperator<String> encoder) {
    
       if (source == null) {
          return null;
       }
       if (source.indexOf('{') == -1) {
          return source;
       }
       if (source.indexOf(':') != -1) {
          source = sanitizeSource(source);
       }
       Matcher matcher = NAMES_PATTERN.matcher(source);
       StringBuffer sb = new StringBuffer();
       while (matcher.find()) {
          String match = matcher.group(1);
          String varName = getVariableName(match);
          Object varValue = uriVariables.getValue(varName);
          if (UriTemplateVariables.SKIP_VALUE.equals(varValue)) {
             continue;
          }
          String formatted = getVariableValueAsString(varValue);
          formatted = encoder != null ? encoder.apply(formatted) : Matcher.quoteReplacement(formatted);
          matcher.appendReplacement(sb, formatted);
       }
       matcher.appendTail(sb);
       return sb.toString();
    }

2、执行http请求

RestTemplate的execute()方法负责发送http请求并解析响应的结果,这个方法首先使用上面提到的UriTemplateHandler将url模板和模板参数转换成URI,然后传递给doExecute()方法完成整个流程的调用。

    @Nullable
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
          @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    
       Assert.notNull(url, "URI is required");
       Assert.notNull(method, "HttpMethod is required");
       ClientHttpResponse response = null;
       try {
          ClientHttpRequest request = createRequest(url, method);
          if (requestCallback != null) {
             requestCallback.doWithRequest(request);
          }
          response = request.execute();
          handleResponse(url, method, response);
          return (responseExtractor != null ? responseExtractor.extractData(response) : null);
       }
       catch (IOException ex) {
          String resource = url.toString();
          String query = url.getRawQuery();
          resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
          throw new ResourceAccessException("I/O error on " + method.name() +
                " request for \"" + resource + "\": " + ex.getMessage(), ex);
       }
       finally {
          if (response != null) {
             response.close();
          }
       }
    }

2.1、构建ClientHttpRequest

202309122024137602.png

在抽象类HttpAccessor中定义了创建ClientHttpRequest的方法和配置ClientHttpRequestFactory的方法,RestTemplate中有个构造方法可以方便的配置ClientHttpRequestFactory,如果没有配置默认使用SimpleClientHttpRequestFactory。

    public RestTemplate(ClientHttpRequestFactory requestFactory) {
       this();
       setRequestFactory(requestFactory);
    }

下面是创建ClientHttpRequest的方法。

    protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
       ClientHttpRequest request = getRequestFactory().createRequest(url, method);
       if (logger.isDebugEnabled()) {
          logger.debug("HTTP " + method.name() + " " + url);
       }
       return request;
    }

创建ClientHttpRequest的任务直接交给了ClientHttpRequestFactory,这个接口只有唯一的方法:

    @FunctionalInterface
    public interface ClientHttpRequestFactory {
       //为指定的URI和HTTP方法创建新的ClientHttpRequest。
       //可以写入请求信息,然后通过调用ClientHttpRequest.execute()来执行。
       ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
    
    }

SimpleClientHttpRequestFactory#createRequest()方法的实现:

    @Override
    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
       HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
       prepareConnection(connection, httpMethod.name());
       //默认true
       if (this.bufferRequestBody) {
          return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
       }
       else {
          return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
       }
    }

SimpleClientHttpRequestFactory底层是使用JDK标准库HttpURLConnection来构建SimpleBufferingClientHttpRequest。

Spring除了提供默认的SimpleClientHttpRequestFactory,还提供OkHttp3ClientHttpRequestFactory底层使用OkHttp3.x框架发送http请求,Netty4ClientHttpRequestFactory底层使用Netty4,HttpComponentsClientHttpRequestFactory底层使用Apache HttpComponents 4.3或更高版本。

2.2、应用RequestCallback

在上一步创建一个ClientHttpRequest后,会调用它的doWithRequest(),AcceptHeaderRequestCallback会设置请求的accept头,HttpEntityRequestCallback写请求体对象到请求流中,前面已给出具体实现不再说了。

2.3、发送http请求

发送http请求是通过调用ClientHttpRequest的execute()方法完成的,下面看一下ClientHttpRequest的体系结构,然后分析一下SimpleBufferingClientHttpRequest的实现原理。

202309122024141083.png

AbstractClientHttpRequest是ClientHttpRequest的抽象基础,确保不会多次写入header和body并且只支持一次http当请求通过assertNotExecuted()方法的限制。此外还实现了HttpOutputMessage接口法,这个接口会在上面提到的HttpEntityRequestCallback中用于完成POST和PUT请求的消息体写入。

    public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
       private final HttpHeaders headers = new HttpHeaders();
       private boolean executed = false;
       @Override
       public final HttpHeaders getHeaders() {
          return (this.executed ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
       }
       @Override
       public final OutputStream getBody() throws IOException {
          //不可以多次调用写body
          assertNotExecuted();
          return getBodyInternal(this.headers);
       }
    
       @Override
       public final ClientHttpResponse execute() throws IOException {
          //http请求不能执行多次
          assertNotExecuted();
          ClientHttpResponse result = executeInternal(this.headers);
          this.executed = true;
          return result;
       }
       //断言此请求尚未执行execute()方法
       protected void assertNotExecuted() {
          Assert.state(!this.executed, "ClientHttpRequest already executed");
       }
       //返回主体的抽象模板方法。
       protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;
       //将给定headers和内容写入HTTP请求的抽象模板方法。
       protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;
    
    }

AbstractBufferingClientHttpRequest实现了getBodyInternal()方法返回一个ByteArrayOutputStream用于缓存写入http请求体,这意味着executeInternal()方法在执行请求时就可以设置contentLength通过ByteArrayOutputStream的字节大小,因此使用它小心内存溢出。

    abstract class AbstractBufferingClientHttpRequest extends AbstractClientHttpRequest {
       private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream(1024);
       @Override
       protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
          return this.bufferedOutput;
       }
       @Override
       protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
          byte[] bytes = this.bufferedOutput.toByteArray();
          if (headers.getContentLength() < 0) {
             headers.setContentLength(bytes.length);
          }
          ClientHttpResponse result = executeInternal(headers, bytes);
          this.bufferedOutput = new ByteArrayOutputStream(0);
          return result;
       }
       //将给定heanders和body写入HTTP请求的抽象模板方法。
       protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput)
             throws IOException;
    }

SimpleBufferingClientHttpRequest会缓存请求体的内容,然后一次性发送。

    final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
       private final HttpURLConnection connection;
       private final boolean outputStreaming;
       SimpleBufferingClientHttpRequest(HttpURLConnection connection, boolean outputStreaming) {
          this.connection = connection;
          this.outputStreaming = outputStreaming;
       }
       @Override
       public String getMethodValue() {
          return this.connection.getRequestMethod();
       }
       @Override
       public URI getURI() {
          try {
             return this.connection.getURL().toURI();
          }
          catch (URISyntaxException ex) {
             throw new IllegalStateException("Could not get HttpURLConnection URI: " + ex.getMessage(), ex);
          }
       }
       @Override
       protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
          addHeaders(this.connection, headers);
          // JDK <1.8 doesn't support getOutputStream with HTTP DELETE
          if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
             this.connection.setDoOutput(false);
          }
          //bufferedOutput总是代表消息体的大小,显示设置提高flush效率
          if (this.connection.getDoOutput() && this.outputStreaming) {
             this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
          }
          this.connection.connect();
          if (this.connection.getDoOutput()) {
             //一次将请求发送完毕
             FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
          }
          else {
             // 在无output场景中立即触发请求
             this.connection.getResponseCode();
          }
          return new SimpleClientHttpResponse(this.connection);
       }
       //将给定的heanders添加到给定的HTTP连接。
       static void addHeaders(HttpURLConnection connection, HttpHeaders headers) {
          headers.forEach((headerName, headerValues) -> {
             if (HttpHeaders.COOKIE.equalsIgnoreCase(headerName)) {  // RFC 6265
                String headerValue = StringUtils.collectionToDelimitedString(headerValues, "; ");
                connection.setRequestProperty(headerName, headerValue);
             }
             else {
                for (String headerValue : headerValues) {
                   String actualHeaderValue = headerValue != null ? headerValue : "";
                   connection.addRequestProperty(headerName, actualHeaderValue);
                }
             }
          });
       }
    }

2.4、异常状态的处理

handleResponse()方法当http相应的状态码是4xx或5xx则会调用ResponseErrorHandler处理这个响应。

    protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
       ResponseErrorHandler errorHandler = getErrorHandler();
       boolean hasError = errorHandler.hasError(response);
       if (logger.isDebugEnabled()) {
          try {
             int code = response.getRawStatusCode();
             HttpStatus status = HttpStatus.resolve(code);
             logger.debug("Response " + (status != null ? status : code));
          }
          catch (IOException ex) {
             // ignore
          }
       }
       if (hasError) {
          errorHandler.handleError(url, method, response);
       }
    }

啊RestTemplate内部默认使用DefaultResponseErrorHandler来处理异常响应。

    public class DefaultResponseErrorHandler implements ResponseErrorHandler {
       @Override
       public boolean hasError(ClientHttpResponse response) throws IOException {
          int rawStatusCode = response.getRawStatusCode();
          HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
          return (statusCode != null ? hasError(statusCode) : hasError(rawStatusCode));
       }
       protected boolean hasError(HttpStatus statusCode) {
          return statusCode.isError();
       }
    
       protected boolean hasError(int unknownStatusCode) {
          HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);
          //4xx || 5xx
          return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
       }
    
       @Override
       public void handleError(ClientHttpResponse response) throws IOException {
          HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
          if (statusCode == null) {
             throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
                   response.getHeaders(), getResponseBody(response), getCharset(response));
          }
          handleError(response, statusCode);
       }
    
       protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
          String statusText = response.getStatusText();
          HttpHeaders headers = response.getHeaders();
          byte[] body = getResponseBody(response);
          Charset charset = getCharset(response);
          switch (statusCode.series()) {
             case CLIENT_ERROR:
                throw HttpClientErrorException.create(statusCode, statusText, headers, body, charset);
             case SERVER_ERROR:
                throw HttpServerErrorException.create(statusCode, statusText, headers, body, charset);
             default:
                throw new UnknownHttpStatusCodeException(statusCode.value(), statusText, headers, body, charset);
          }
       }
    
       @Deprecated
       protected HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
          HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
          if (statusCode == null) {
             throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(),
                   response.getHeaders(), getResponseBody(response), getCharset(response));
          }
          return statusCode;
       }
    
       protected byte[] getResponseBody(ClientHttpResponse response) {
          try {
             return FileCopyUtils.copyToByteArray(response.getBody());
          }
          catch (IOException ex) {
             // ignore
          }
          return new byte[0];
       }
    
       @Nullable
       protected Charset getCharset(ClientHttpResponse response) {
          HttpHeaders headers = response.getHeaders();
          MediaType contentType = headers.getContentType();
          return (contentType != null ? contentType.getCharset() : null);
       }
    
    }

2.5、提取response数据转换成自定义实体

在调用ClientHttpRequest的execute()方法后返回了一个封装http response的ClientHttpResponse,ResponseExtractor用来从ClientHttpResponse提取数据转换成泛型参数所代表的对象。

    public <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) {
       return new ResponseEntityResponseExtractor<>(responseType);
    }

ResponseEntityResponseExtractor内部委托给HttpMessageConverterExtractor。

    private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> {
       @Nullable
       private final HttpMessageConverterExtractor<T> delegate;
       public ResponseEntityResponseExtractor(@Nullable Type responseType) {
          if (responseType != null && Void.class != responseType) {
             this.delegate = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
          }
          else {
             this.delegate = 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();
          }
       }
    }

HttpMessageConverterExtractor在1.1小节看过了这里不再啰嗦。

3、拦截器

拦截器可以在发送http请求之前对请求进行访问或修改,这一切对用户是透明的,只需要编写我们的ClientHttpRequestInterceptor即可。

    @FunctionalInterface
    public interface ClientHttpRequestInterceptor {
       ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
             throws IOException;
    }

3.1、拦截器原理

RestTemplate的父类InterceptingHttpAccessor通过调用setInterceptors()方法提供了添加拦截器的功能。

    public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
       // Take getInterceptors() List as-is when passed in here
       if (this.interceptors != interceptors) {
          this.interceptors.clear();
          this.interceptors.addAll(interceptors);
          AnnotationAwareOrderComparator.sort(this.interceptors);
       }
    }

InterceptingHttpAccessor也重写了getRequestFactory()方法,目的是如果配置了拦截器,则使用InterceptingClientHttpRequestFactory,这个类重写了父类的createRequest()方法返回InterceptingClientHttpRequest,这个类持有拦截器的引用。

InterceptingHttpAccessor#getRequestFactory()方法:

    @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();
       }
    }
    public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
       private final List<ClientHttpRequestInterceptor> interceptors;
       public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
             @Nullable List<ClientHttpRequestInterceptor> interceptors) {
          super(requestFactory);
          this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());
       }
       @Override
       protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
          return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
       }
    }

应为拦截器中可以获取到request的body,所以InterceptingClientHttpRequest继承于AbstractBufferingClientHttpRequest,拦截器是在executeInternal()方法中起作用的。

    @Override
    protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
       InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
       return requestExecution.execute(this, bufferedOutput);
    }
    
    private class InterceptingRequestExecution implements ClientHttpRequestExecution {
       private final Iterator<ClientHttpRequestInterceptor> iterator;
       public InterceptingRequestExecution() {
          this.iterator = interceptors.iterator();
       }
       @Override
       public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
          if (this.iterator.hasNext()) {
             ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
             //拦截器起作用后内部会调用execution.execute()方法继续调用这个方法使下个拦截器起作用
             return nextInterceptor.intercept(request, body, this);
          }
          else {
             //直到所有拦截器拦截完毕,使用原requestFactory创建的ClientHttpRequest完成请求
             HttpMethod method = request.getMethod();
             Assert.state(method != null, "No standard HTTP method");
             ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
             request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
             if (body.length > 0) {
                if (delegate instanceof StreamingHttpOutputMessage) {
                   StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
                   streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
                }
                else {
                   StreamUtils.copy(body, delegate.getBody());
                }
             }
             return delegate.execute();
          }
       }
    }

3.2、Spring提供的拦截器实现

BasicAuthenticationInterceptor 可以对http header添加basic-auth提供客户端的Basic认证。

    public class BasicAuthenticationInterceptor implements ClientHttpRequestInterceptor {
       private final String username;
       private final String password;
       @Nullable
       private final Charset charset;
       public BasicAuthenticationInterceptor(String username, String password) {
          this(username, password, null);
       }
       public BasicAuthenticationInterceptor(String username, String password, @Nullable Charset charset) {
          Assert.doesNotContain(username, ":", "Username must not contain a colon");
          this.username = username;
          this.password = password;
          this.charset = charset;
       }
       @Override
       public ClientHttpResponse intercept(
             HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
          HttpHeaders headers = request.getHeaders();
          if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
             headers.setBasicAuth(this.username, this.password, this.charset);
          }
          return execution.execute(request, body);
       }
    }

Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文