• RestTemplate源码解析



    上一篇文章,我们介绍了RestTemplate在各种场景下的使用,今天我们来分析一下它的源码。

    类结构

    在这里插入图片描述
    RestOperations是一个接口,意为Rest操作,抽象出了具有restful风格的操作方法,我们常用的getForObject、postForEntity、exchange都是里面定义的接口方法。

    HttpAccessor则是一个抽象类,意为Http访问器,内部保存了请求的工厂类,即ClientHttpRequestFactory类,使用createRequest生产ClientHttpRequest,RestTemplate最终会将请求构造成ClientHttpRequest,由ClientHttpRequest负责与服务端进行交互。

    流程解析

    入口:

    result = restTemplate.getForObject("http://localhost:9001/producer", String.class);
    
    • 1

    getForObject是RestOperation中的方法

    public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
           //获取请求回调
    	RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
           //获取http消息转化抽取器,HttpMessageConverterExtractor实现了ResponseExtractor,可以将http响应的文本数据转化成相应的java对象。
    	HttpMessageConverterExtractor<T> responseExtractor =
    			new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    	return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    acceptHeaderRequestCallback(responseType)

    	public <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
    		// 返回了AcceptHeaderRequestCallback实例
    		return new AcceptHeaderRequestCallback(responseType);
    	}
    
    • 1
    • 2
    • 3
    • 4

    进入execute方法中

    	public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
    			@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
            //拼接uri
    		URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    		return doExecute(expanded, method, requestCallback, responseExtractor);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    进入doExecute中【核心代码

    	protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
    			@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
     		// url和请求方法不能为null
    		Assert.notNull(url, "URI is required");
    		Assert.notNull(method, "HttpMethod is required");
    		ClientHttpResponse response = null;
    		try {
                //创建文章开头所说的ClientHttpRequest 
    			ClientHttpRequest request = createRequest(url, method);
    			if (requestCallback != null) {
                    //执行请求回调
    				requestCallback.doWithRequest(request);
    			}
                //执行请求,获取响应结果
    			response = request.execute();
                //处理响应结果
    			handleResponse(url, method, response);
                //利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
    			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();
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    createRequest(url, method),此处调用的是HttpAccessor中的createRequest方法

        protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
            //this.getRequestFactory()返回默认的SimpleClientHttpRequestFactory
            ClientHttpRequest request = this.getRequestFactory().createRequest(url, method);
            this.initialize(request);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("HTTP " + method.name() + " " + url);
            }
     
            return request;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中getRequestFactory方法被InterceptingHttpAccessor(在类图结构中有该类)重写了

    	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();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以看到,一上来先获取拦截器,不过我们并没有设置拦截器。因此直接返回了父类即HttpAccessor中的RequestFactory

    public abstract class HttpAccessor {
     
    	/** Logger available to subclasses. */
    	protected final Log logger = HttpLogging.forLogName(getClass());
     
    	private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
      
        .....
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    而HttpAccessor中使用的是SimpleClientHttpRequestFactory类。

    接着走进SimpleClientHttpRequestFactory的createRequest方法

    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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    openConnection(uri.toURL(), this.proxy)

    	protected HttpURLConnection openConnection(URL url, @Nullable Proxy proxy) throws IOException {
    		URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
    		if (!(urlConnection instanceof HttpURLConnection)) {
    			throw new IllegalStateException(
    					"HttpURLConnection required for [" + url + "] but got: " + urlConnection);
    		}
    		return (HttpURLConnection) urlConnection;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    到这里,仿佛明白了什么,原来RestTemplate对原生的http请求URLConnection 进行了一层封装。
    prepareConnection(connection, httpMethod.name())

    	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);
    		}
            //设置请求方法等
    		boolean mayWrite =
    				("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
    						"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod));
     
    		connection.setDoInput(true);
    		connection.setInstanceFollowRedirects("GET".equals(httpMethod));
    		connection.setDoOutput(mayWrite);
    		connection.setRequestMethod(httpMethod);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    prepareConnection比较简单,进行了一些简单的设置工作。当我们自己利用URLConnection进行http请求时,这些设置也是我们必须要去做的,RestTemplate只是封装好了罢了

    createRequest流程走完了,返回了一个封装URLConnection的ClientHttpRequest对象

    接下来到了requestCallback.doWithRequest(request),即在执行请求之前,先去执行请求回调的doWithRequest方法

    public void doWithRequest(ClientHttpRequest request) throws IOException {
    	if (this.responseType != null) {
    		List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
    				.filter(converter -> canReadResponse(this.responseType, converter))
    				.flatMap((HttpMessageConverter<?> converter) -> getSupportedMediaTypes(this.responseType, converter))
    				.distinct()
    				.sorted(MediaType.SPECIFICITY_COMPARATOR)
    				.collect(Collectors.toList());
    		if (logger.isDebugEnabled()) {
    			logger.debug("Accept=" + allSupportedMediaTypes);
    		}
    		request.getHeaders().setAccept(allSupportedMediaTypes);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    接下来需要真正去执行调用了,即request.execute()

    execute方法在AbstractClientHttpRequest类中

    	public final ClientHttpResponse execute() throws IOException {
            //检查ClientHttpRequest是否被执行过,如果执行过,则直接报错
    		assertNotExecuted();
    		ClientHttpResponse result = executeInternal(this.headers);
            //在执行请求之后,将标志位设置为true
    		this.executed = true;
    		return result;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    进入executeInternal方法中,位于AbstractBufferingClientHttpRequest中

    	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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其中核心的是executeInternal(headers, bytes)方法,位于SimpleBufferingClientHttpRequest中

    	protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
            //将请求头设置进connection中
    		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()) {
                //将缓存区的数据复制进connection的输出流中
    			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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    该方法使用HttpURLConnection与服务端建立了连接,并返回一个SimpleClientHttpResponse。单纯包裹着此HttpURLConnection对象。

    request.execute()也就走完了,接着走主流程的下一步

    handleResponse(url, method, response)

    	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
    			}
    		}
            //如果出现错误的话,例如状态码为500,则进入到错误处理流程中
    		if (hasError) {
    			errorHandler.handleError(url, method, response);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这一步主要是检查状态码是否是4或者5开头的,如果是的话,则走相应的错误处理流程。

    如果没有错误的话,则进入到最后一步

    responseExtractor.extractData(response),抽取响应中的数据

    responseExtractor是HttpMessageConverterExtractor类型的

    	public T extractData(ClientHttpResponse response) throws IOException {
    		MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
    		if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
    			return null;
    		}
    		MediaType contentType = getContentType(responseWrapper);
     
    		try {
                //遍历HttpMessageConverter
    			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) {
                        //如果该messageConverter能够读取该contentType,并且能转化成responseClass类型
    					if (messageConverter.canRead(this.responseClass, contentType)) {
    						if (logger.isDebugEnabled()) {
    							String className = this.responseClass.getName();
    							logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
    						}
                            //将响应数据转化成对应的java类型
    						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 UnknownContentTypeException(this.responseType, contentType,
    				response.getRawStatusCode(), response.getStatusText(), response.getHeaders(),
    				getResponseBody(response));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    extractData和核心逻辑就是,遍历所有的HttpMessageConverter,如果发现某个messageConverter能够读取响应数据,则返回转化后的数据。

    主流程最后一步,则是将封装的HttpURLConnection的输入流关闭。

    到这里,RestTemplate的源码分析就结束了。

    总结

    一句话概括RestTemplate,RestTemplate封装了原生的HttpURLConnection,采用Restful的理念,更优雅地来完成对HTTP服务的调用。

  • 相关阅读:
    CaptchaUtil工具类生成GIF四则运算验证码
    爱上开源之dockerUI强大docker管理工具,没有之一
    总交易量突破 3000 亿美元,APX Finance 成本轮牛市最大的黑马?
    C++指针解读(8) -- 指针数组和二重指针
    Jenkins-SpringBoot-实现自动化构建
    数据结构实验6 :图的存储与遍历(邻接矩阵的深度优先遍历DFS和邻接表的广度优先遍历BFS)
    Leetcode 71. 简化路径
    端口被占用?两步解决端口占用问题
    【0234】PgBackendStatus 记录当前postgres进程的活动状态
    Spring读书笔记——bean创建(上)
  • 原文地址:https://blog.csdn.net/CSDN_SAVIOR/article/details/126287826