RestTemplate源码分析

相信RestTemplate是我们在SpringBoot中用过的最多的http客户端,今天来分析他的源码。

一、最简单的用法

首先需要向容器中注入RestTemplate的Bean实例

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}

接着直接使用即可

@RestController
public class Controller {

    @Autowired
    RestTemplate restTemplate;

    @RequestMapping("/test")
    public String test() {
        String result = restTemplate.getForObject("https://www.baidu.com/", String.class);
        System.out.println(result);
        return result;
    }
}

可以看到,RestTemplate简化了http请求操作,甚至只需要传入一个url与响应的类型,即可获取结果。

二、RestTemplate类图结构

类图结构是我们直观认识一个类最有效的工具,很多人可能都不知道怎么看类图。

只需要简单的三步

 那么他的类图结构是这样子的:

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

如图:

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

其中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;
    }

 InterceptingHttpAccessor是HttpAccessor的实现类,主要是增加了一些有关拦截器的逻辑,这个本文就不再赘述。

三、RestTemplate的构造方法

看源码之前,大家可以先去下载Spring框架的源码,鉴于github速度感人,可以去码云上下载https://gitee.com/mirrors/Spring-Framework自己感兴趣的版本,记得先安装好Gradle环境哦,Gradle的环境安装与配置,当然idea也有自带的Gradle。

无参构造方法如下:

	public RestTemplate() {
		this.messageConverters.add(new ByteArrayHttpMessageConverter());
		this.messageConverters.add(new StringHttpMessageConverter());
		this.messageConverters.add(new ResourceHttpMessageConverter(false));
		if (!shouldIgnoreXml) {
			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 (!shouldIgnoreXml) {
			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());
		}
		else if (kotlinSerializationJsonPresent) {
			this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
		}

		if (jackson2SmilePresent) {
			this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
		}
		if (jackson2CborPresent) {
			this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
		}

		this.uriTemplateHandler = initUriTemplateHandler();
	}

构造方法主要做了两件事:

添加了很多HttpMessageConverter的实现类,HttpMessageConverter的各个实现类主要是将请求响应的文本转化成相应的java对象,想要了解更多关于HttpMessageConverter的信息,可以参考这篇文章HttpMessageConverter那回事

初始化了uri模版处理器,这个处理器主要对uri做一些拼接操作,例如在get请求中,将param拼接到url的后面。

四、执行请求的核心逻辑

我们以一开始的代码为例,逐步进入到核心逻辑中。

restTemplate.getForObject("https://www.baidu.com/", String.class)

从上面中可以了解到,getForObject是RestOperation中的方法,RestTemplate的实现为:

	public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
        //获取请求回调
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
        //获取http消息转化抽取器
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
	}

走进 acceptHeaderRequestCallback(responseType)

	public <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) {
		return new AcceptHeaderRequestCallback(responseType);
	}

返回了AcceptHeaderRequestCallback实例,这个对象的doWithRequest会在后面用到,先放着。

HttpMessageConverterExtractor实现了ResponseExtractor,可以将http响应的文本数据转化成相应的java对象。

接着进入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);
	}

这个很简单,直接进入doExecute中,该方法就是RestTemplate的主流程代码,比较核心。

	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 
			ClientHttpRequest request = createRequest(url, method);
			if (requestCallback != null) {
                //执行请求回调
				requestCallback.doWithRequest(request);
			}
            //执行请求,U获取响应结果
			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();
			}
		}
	}

其中涉及到多个方法:

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

其中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();
		}
	}

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

public abstract class HttpAccessor {

	/** Logger available to subclasses. */
	protected final Log logger = HttpLogging.forLogName(getClass());

	private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
  
    .....
}

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

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

到这里,仿佛明白了什么,原来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);
	}

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

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

我把doExecute方法再抄一遍,一方面为了不用再翻上去,一方面也能加深大家的印象。

	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 
			ClientHttpRequest request = createRequest(url, method);
			if (requestCallback != null) {
                //执行请求回调
				requestCallback.doWithRequest(request);
			}
            //执行请求,U获取响应结果
			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();
			}
		}
	}

接下来到了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);
			}
		}

 这段代码的主要逻辑,是将支持的MediaType设置进请求头中

回到主流程中,接下来需要真正去执行调用了,即request.execute(),

前面了解到,这个request是SimpleBufferingClientHttpRequest类型的,先看一下该类的类图结构

 execute方法在AbstractClientHttpRequest类中

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

进入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;
	}

其中核心的是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);
	}

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

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

再贴一遍主流程

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

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

这一步主要是检查状态码是否是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));
	}

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

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

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

一句话概括RestTemplate,

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

猜你喜欢

转载自blog.csdn.net/qq_33591903/article/details/119484729