zoukankan      html  css  js  c++  java
  • Spring

    e8fd3caf5a180e7f9110639848ed548d.jpg

    什么是RestTemplate

    Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others.

    这是RestTemplate源码里对其自身的解释,从类名来看把类想设计成一个标准的模板, 简单来说就是简化Http的请求以及响应的封装,并且执行了Restful原则。如果没有RestTemplate,我们可能使用更多的还是Apache HttpClient工具。 另外从这个定义里我看到一个很重要的类HttpUrlConnection,这是RestTemplate与HTTP服务器通信的核心类。

    RestTempate类结构

    截屏2020-08-1420.46.03.png
    从类库中可以看出,这个类是一个很Spring的设计,继承抽象类InterceptingHttpAccessor,实现接口RestOperations。

    RestTemplate执行流程图

    开始分析之前,我们以RestTemplate#getForObject()为例,先浏览一下整个方法调用过程的执行流程图,几个关键步骤,看到其中的几个关键类,我会在后面详细说明这几个关键类。
    SpringCloud RestTemplate 拦截器.jpg

    RestTemplate构造函数

    在看具体的执行http请求方法前,我们先看一下构造函数都做了那些工具。

    public RestTemplate() {
        #设置各种messageConvert, 比如我们最常见的StringHttpMessageConverter。
        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());
        }
        #设置UriTemplateHandler
        this.uriTemplateHandler = initUriTemplateHandler();
    }
    
    

    构造方法总结来说只做了两件事,添加HttpMessageConvert实现类,手动配置SpringMVC的时代,想必大家都知道HttpMessageConverter吧,顾名思义就是转换HTTP请求响应过程中的消息数据。第二就是初始化UriTemplateHandler,在initUriTemplateHanlder()方法中可以看到实际实例化的是DefaultUriBuilderFactory类并返回。


    RestTemplat#getForObject()

    我们现在开始沿着getForObject入口来分析一下执行一个HTTP Rest请求的流程到底是怎样的哈。

    getForObject

    #RestTemplate.getForObject
    @Override
    @Nullable
    public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
       RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
       HttpMessageConverterExtractor<T> responseExtractor =
    		new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
       #这里传入requestCallback和responseExtractor,调用execute()
       return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }
    

    execute()

    #RestTemplate.execute
    @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);
      #调用doExecute()
      return doExecute(expanded, method, requestCallback, responseExtractor);
    }
    

    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 {
            #1.生成请求
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                #2.设置header
                requestCallback.doWithRequest(request);
            }
            #3.执行请求
            response = request.execute();
            #4.处理响应
            handleResponse(url, method, response);
            #5.返回执行结果
            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();
            }
        }
    }
    

    HttpAccessor

    doExecute()方法的第一个步骤就是创建ClientHttpRequest, 这是一个接口,那么这个方法执行完会创建那个实现类呢? createRequest() 是父类HttpAccessor的方法,我们要先简单分析下HttpAcessor。

    package org.springframework.http.client.support;
    
    import java.io.IOException;
    import java.net.URI;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.commons.logging.Log;
    
    import org.springframework.core.annotation.AnnotationAwareOrderComparator;
    import org.springframework.http.HttpLogging;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.client.ClientHttpRequest;
    import org.springframework.http.client.ClientHttpRequestFactory;
    import org.springframework.http.client.ClientHttpRequestInitializer;
    import org.springframework.http.client.SimpleClientHttpRequestFactory;
    import org.springframework.util.Assert;
    
    public abstract class HttpAccessor {
    
        protected final Log logger = HttpLogging.forLogName(getClass());
    
    	private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
    
    	private final List<ClientHttpRequestInitializer> clientHttpRequestInitializers = new ArrayList<>();
    
    	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
    		Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
    		this.requestFactory = requestFactory;
    	}
    
    	public ClientHttpRequestFactory getRequestFactory() {
    		return this.requestFactory;
    	}
    
    	public void setClientHttpRequestInitializers(
    			List<ClientHttpRequestInitializer> clientHttpRequestInitializers) {
    
    		if (this.clientHttpRequestInitializers != clientHttpRequestInitializers) {
    			this.clientHttpRequestInitializers.clear();
    			this.clientHttpRequestInitializers.addAll(clientHttpRequestInitializers);
    			AnnotationAwareOrderComparator.sort(this.clientHttpRequestInitializers);
    		}
    	}
        
    	public List<ClientHttpRequestInitializer> getClientHttpRequestInitializers() {
    		return this.clientHttpRequestInitializers;
    	}
    
        #在这个方法里会创建ClientHttpRequest实例并且返回。
    	protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
    		ClientHttpRequest request = getRequestFactory().createRequest(url, method);
    		initialize(request);
    		if (logger.isDebugEnabled()) {
    			logger.debug("HTTP " + method.name() + " " + url);
    		}
    		return request;
    	}
    
    	private void initialize(ClientHttpRequest request) {
    		this.clientHttpRequestInitializers.forEach(initializer -> initializer.initialize(request));
    	}
    }
    

    从HttpAccessor#createRequest()分析来看,会先调用getRequestFactory() 返回_SimpleClientHttpRequestFactory,然后调用SimpleClientHttpRequestFactory.createRequest()。但是这里我们忽略了一个环节,那就是**InterceptingHttpAccessor, **_这里我们需要再深吸一口气,再看看InterceptingHttpAccessor类。

    SimpleClientHttpRequestFactory类结构图

    截屏2020-08-1421.53.41.png

    InterceptingHttpAccessor

    public abstract class InterceptingHttpAccessor extends HttpAccessor {
        
        #拦截器属性,可以自定义拦截器实现业务逻辑,当然玩过SpringCloud Ribbon调用,对这个属性再熟悉不过了。
    	private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
    
        #这个属性要和HttpAccessor.requestFactory呼应一下。
    	@Nullable
    	private volatile ClientHttpRequestFactory interceptingRequestFactory;
    
    	public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
    		if (this.interceptors != interceptors) {
    			this.interceptors.clear();
    			this.interceptors.addAll(interceptors);
    			AnnotationAwareOrderComparator.sort(this.interceptors);
    		}
    	}
    
    	public List<ClientHttpRequestInterceptor> getInterceptors() {
    		return this.interceptors;
    	}
    
    	@Override
    	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
    		super.setRequestFactory(requestFactory);
    		this.interceptingRequestFactory = null;
    	}
    
    
    	@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();
    		}
    	}
    }
    

    这个类的关键在于重写了父类的getRequestFacotry(), 当前做过SpringCloud Ribbon远程调用,会对这个方法格外熟悉,Spring会判断拦截器属性_interceptors是否有值,如果有值则会继续判断,然后返回InterceptionClientHttpRequestFactory, 如果拦截器属性没有值,则调用父类HttpAccessor#getRequestFactory().



    RestTemplate#doExecute()

    通过分析抽象父类InterceptingHttpAccessor和HttpAccessor, 我们得出结果,如果没有拦截器,只是普通的RESTFUL调用,那么最终是调用SimpleClientHttpRequestFactory#createRequest().

    @Override
    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
        #再回头看看Spring对RestTemplate定义里提到的,RestTemplate的核心就是使用
        #HttpURLConnection和HTTP服务器进行通信。
        HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
        prepareConnection(connection, httpMethod.name());
    
        if (this.bufferRequestBody) {
            return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
        }
        else {
            return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
        }
    }
    
    protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
    	URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
        if (!HttpURLConnection.class.isInstance(urlConnection)) {
            throw new IllegalStateException("HttpURLConnection required for [" + url + "] but got: " + urlConnection);
        }
        return (HttpURLConnection) urlConnection;
    }
    
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        if (this.connectTimeout >= 0) {
            connection.setConnectTimeout(this.connectTimeout);
        }
        if (this.readTimeout >= 0) {
            connection.setReadTimeout(this.readTimeout);
        }
    
        connection.setDoInput(true);
    
        if ("GET".equals(httpMethod)) {
            connection.setInstanceFollowRedirects(true);
        }
        else {
            connection.setInstanceFollowRedirects(false);
        }
    
        if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
            "PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
            connection.setDoOutput(true);
        }
        else {
            connection.setDoOutput(false);
        }
    
    	connection.setRequestMethod(httpMethod);
    }
    

    从这里代码片段分析,我们可以回到上面提的问题, 在doExecute里createRequest()最终创建的实现类是SimpleBufferingClientHttpRequest。

    SimpleBufferingClientHttpRequest类结构图

    截屏2020-08-1421.55.50.png

    SimpleStreamingClientHttpRequest类结构图

    截屏2020-08-1421.56.54.png

    requestCallback.doWithRequest()

    根据自己需要实现接口RequestCallback#doWithRequest()。

    request.execute()

    这里也就是调用SimplezBufferingClientHttpRequest#execute()。通过分析SimplezBufferingClientHttpRequest代码,我们知道execute()定义在抽象父类AbstractClientHttpRequest里

    #AbstractClientHttpRequest#execute()
    @Override
    public final ClientHttpResponse execute() throws IOException {
        assertNotExecuted();
        ClientHttpResponse result = executeInternal(this.headers);
        this.executed = true;
        return result;
    }
    

    分析这个类可以看出,很经典的一个设计,父类定义行为,具体实现交给子类,这里可以看出execute里继续调用executeInternal,这是一个抽象方法,交给子类实现。

    AbstractBufferingClientHttpRequest#executeInternal

    @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;
    }
    

    SimpleBufferingClientHttpRequest#executeInternal

    @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);
        }
        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 {
            // Immediately trigger the request in a no-output scenario as well
            this.connection.getResponseCode();
        }
        return new SimpleClientHttpResponse(this.connection);
    }
    

    至此,我们就拿到请求的返回结果Response了,封装SimpleClientHttpResponse并返回。似乎这几个类分析下来觉得他的调用流程并没有想象的那么复杂,整个设计还是很规范。

    handleResponse

    RestTemplate#handleResponse

    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);
        }
    }
    

    getErrorHandler获取了一个错误处理器,如果Response的状态码是错误的,那么就调用handleError处理错误并抛出异常。

    responseExtractor.extractData

    ResponseExtractor是一个函数式接口,主要实现类有三个。

    • HeadersExtractor - RestTemplat的内部类。
    • ResponseEntityResponseExtractor - RestTemplate内部类。
    • HttpMessageConverterExtractor。

    前两个都是内部类,我们主要看一下HttpMessageConverterExtractor。

    @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 + "]");
    }
    
    

    可以看到,extractData先将response交给responseWrapper,如果responseWrapper有消息体且非空,则进行返回消息的读取操作。
    消息的读取需要借助HttpMessageConverter接口,HttpMessageConverter具有多种实现类,以完成不同格式消息的读取,相当于消息解码器或转换头。

    总结

    RestTemplate提供了多种便捷访问HTTP服务的方法,提高了客户端的编码效率。底层通过使用java.net包创建HTTP请求。主要使用ClientHttpRequestFactory指定不同的HTTP请求方式。

  • 相关阅读:
    解决svn Authorization failed错误
    jQuery切换事件
    jQuery学习笔记
    EAS开发
    JavaScript第二课
    JavaScript学习第一课
    EAS常用工具类
    [转]OpenBLAS项目与矩阵乘法优化
    [转]mmap和madvise的使用
    [转]C++赋值运算符重载函数(operator=)
  • 原文地址:https://www.cnblogs.com/sword-successful/p/13505610.html
Copyright © 2011-2022 走看看