zoukankan      html  css  js  c++  java
  • http调用之RestTemplate

    核心方法为org.springframework.web.client.RestTemplate.doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>)

    方法内容如下:

     其中,重点在下面这三行

     ClientHttpRequest是http请求调用的抽象,具体实现有jdk自带的以及apache的httpclient,实现类如下:

    simple开头的是使用jdk自带的net操作,Components的底层是httpclient操作。

     AbstractBufferingClientHttpRequest为缓存requestbody的基类,内部bufferedOutput保存了requestbody的内容,避免流的二次读取会导致读取不到数据。

    那么,到底是哪里给这个ByteArrayOutputStream对象赋值的呢?答案就在org.springframework.web.client.RestTemplate.HttpEntityRequestCallback,注释讲得很清楚:

     在上面讲到的重点强调的三行代码中,调用了org.springframework.web.client.RestTemplate.HttpEntityRequestCallback.doWithRequest(ClientHttpRequest)方法,这个方法里面根据requestContentType等信息,在HttpMessageConverters列表中寻找合适的HttpMessageConverter,利用这些httpMessageConverter天然的write方法将requestBody的内容经过处理转换写入入参HttpOutputMessage对应的getBody方法返回的OutputStream流中。然而,我们这里的ClientHttpRequest接口继承自HttpOutputMessage,AbstractClientHttpRequest的getBody实现调用子类的getBodyInternal方法,重点来了,AbstractBufferingClientHttpRequest的getBodyInternal返回的就是bufferedOutput,也就是缓存的ByteArrayOutputStream。到这里,缓存的requestbody数据有了。再啰嗦一句,使用HttpEntityRequestCallback的都是post请求的,get请求用的AcceptHeaderRequestCallback。

    那么,问题来了,什么情况下会用这个缓存数据呢?我们看下AbstractBufferingClientHttpRequest的子类就知道了。

    这里,又要回到刚刚的三行重点代码,doExecute的时候调用createRequest方法返回ClientHttpRequest,实际调用父类方法org.springframework.http.client.support.HttpAccessor.createRequest(URI, HttpMethod):

     org.springframework.http.client.support.InterceptingHttpAccessor.getRequestFactory()方法如下:

     如果设置了拦截器就返回InterceptingClientHttpRequestFactory,并将restTemplete原有的RequestFactory作为参数传入。

    该factory创建的是org.springframework.http.client.InterceptingClientHttpRequest,该类重写的executeInternal方法通过内部类InterceptingRequestExecution实现interceptors顺序执行,当迭代结束后,通过之前传进来的requestFactory创建ClientHttpRequest,并将body copy给刚刚创建的request的body,并调用execute方法,方法内容如下:

    其实,InterceptingClientHttpRequest本身就带有requestbody缓存copy功能,所以无需在创建restTemplate时包装一个BufferingClientHttpRequestFactory,直接使用HttpComponentsClientHttpRequestFactory即可,这个factory自带bufferRequestBody功能。

    相信大部分同学在项目中都是选择apache的httpclient发送http请求,所以,这里我们重点看下org.springframework.http.client.HttpComponentsClientHttpRequestFactory,我们看下createRequest方法:

    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
            HttpClient client = getHttpClient();
    
            HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
            postProcessHttpRequest(httpRequest);
            HttpContext context = createHttpContext(httpMethod, uri);
            if (context == null) {
                context = HttpClientContext.create();
            }
    
            // Request configuration not set in the context
            if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
                // Use request configuration given by the user, when available
                RequestConfig config = null;
                if (httpRequest instanceof Configurable) {
                    config = ((Configurable) httpRequest).getConfig();
                }
                if (config == null) {
                    config = createRequestConfig(client);
                }
                if (config != null) {
                    context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
                }
            }
         // 默认情况下bufferRequestBody为true,创建使用缓存的body作为请求体
            if (this.bufferRequestBody) {
                return new HttpComponentsClientHttpRequest(client, httpRequest, context);
            }
            else {
                return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context);
            }
        }

    看下上面注释的类的下面方法org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpHeaders, byte[]):

     这个类也是继承自AbstractBufferingClientHttpRequest,所以刚刚InterceptingRequestExecution迭代结束后getBody返回的就是缓存的bufferedOutput,到这里执行的时候就将bufferedOutput作为ByteArrayEntity发送http请求。至此,结束。

  • 相关阅读:
    centos7安装docker
    spring成神之路第四十三篇:spring 中编程式事务怎么用的?
    spring成神之路第四十二篇:玩转 JdbcTemplate
    spring成神之路第四十一篇:@EnableCaching 集成 redis 缓存
    spring成神之路第四十篇:缓存使用(@EnableCaching、@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig)
    spring成神之路第三十八篇:@Scheduled & @EnableScheduling 定时器详解
    spring成神之路第三十七篇:@EnableAsync & @Async 实现方法异步调用
    spring成神之路第三十六篇:@EnableAspectJAutoProxy、@Aspect 中通知顺序详解
    spring成神之路第三十五篇:@Aspect 中 5 中通知详解
    spring成神之路第三十四篇:@Aspect 中@Pointcut 12 种用法
  • 原文地址:https://www.cnblogs.com/restart30/p/11528747.html
Copyright © 2011-2022 走看看