RestTemplate源码分析

本文详细分析了SpringBoot中RestTemplate的使用、类图结构、构造方法以及执行HTTP请求的核心逻辑。RestTemplate通过封装HttpURLConnection,简化了HTTP请求操作,提供了如getForObject等便捷方法。在执行请求时,它会根据响应类型选择合适的HttpMessageConverter进行数据转换。通过对源码的剖析,我们可以更深入理解RestTemplate如何优雅地处理HTTP调用。

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

一、最简单的用法

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


   
  1. @Configuration
  2. public class RestTemplateConfig {
  3. @Bean
  4. public RestTemplate getRestTemplate () {
  5. return new RestTemplate();
  6. }
  7. }

接着直接使用即可


   
  1. @RestController
  2. public class Controller {
  3. @Autowired
  4. RestTemplate restTemplate;
  5. @RequestMapping("/test")
  6. public String test () {
  7. String result = restTemplate.getForObject( "https://www.baidu.com/", String.class);
  8. System.out.println(result);
  9. return result;
  10. }
  11. }

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

二、RestTemplate类图结构

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

只需要简单的三步

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

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

如图:

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

其中createRequest方法如下


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

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

三、RestTemplate的构造方法

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

无参构造方法如下:


   
  1. public RestTemplate () {
  2. this.messageConverters.add( new ByteArrayHttpMessageConverter());
  3. this.messageConverters.add( new StringHttpMessageConverter());
  4. this.messageConverters.add( new ResourceHttpMessageConverter( false));
  5. if (!shouldIgnoreXml) {
  6. try {
  7. this.messageConverters.add( new SourceHttpMessageConverter<>());
  8. }
  9. catch (Error err) {
  10. // Ignore when no TransformerFactory implementation is available
  11. }
  12. }
  13. this.messageConverters.add( new AllEncompassingFormHttpMessageConverter());
  14. if (romePresent) {
  15. this.messageConverters.add( new AtomFeedHttpMessageConverter());
  16. this.messageConverters.add( new RssChannelHttpMessageConverter());
  17. }
  18. if (!shouldIgnoreXml) {
  19. if (jackson2XmlPresent) {
  20. this.messageConverters.add( new MappingJackson2XmlHttpMessageConverter());
  21. }
  22. else if (jaxb2Present) {
  23. this.messageConverters.add( new Jaxb2RootElementHttpMessageConverter());
  24. }
  25. }
  26. if (jackson2Present) {
  27. this.messageConverters.add( new MappingJackson2HttpMessageConverter());
  28. }
  29. else if (gsonPresent) {
  30. this.messageConverters.add( new GsonHttpMessageConverter());
  31. }
  32. else if (jsonbPresent) {
  33. this.messageConverters.add( new JsonbHttpMessageConverter());
  34. }
  35. else if (kotlinSerializationJsonPresent) {
  36. this.messageConverters.add( new KotlinSerializationJsonHttpMessageConverter());
  37. }
  38. if (jackson2SmilePresent) {
  39. this.messageConverters.add( new MappingJackson2SmileHttpMessageConverter());
  40. }
  41. if (jackson2CborPresent) {
  42. this.messageConverters.add( new MappingJackson2CborHttpMessageConverter());
  43. }
  44. this.uriTemplateHandler = initUriTemplateHandler();
  45. }

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

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

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

四、执行请求的核心逻辑

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

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

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


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

走进 acceptHeaderRequestCallback(responseType)


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

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

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

接着进入execute方法中


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

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


   
  1. protected <T> T doExecute (URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
  2. @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
  3. Assert.notNull(url, "URI is required");
  4. Assert.notNull(method, "HttpMethod is required");
  5. ClientHttpResponse response = null;
  6. try {
  7. //创建文章开头所说的ClientHttpRequest
  8. ClientHttpRequest request = createRequest(url, method);
  9. if (requestCallback != null) {
  10. //执行请求回调
  11. requestCallback.doWithRequest(request);
  12. }
  13. //执行请求,U获取响应结果
  14. response = request.execute();
  15. //处理响应结果
  16. handleResponse(url, method, response);
  17. //利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
  18. return (responseExtractor != null ? responseExtractor.extractData(response) : null);
  19. }
  20. catch (IOException ex) {
  21. String resource = url.toString();
  22. String query = url.getRawQuery();
  23. resource = (query != null ? resource.substring( 0, resource.indexOf( '?')) : resource);
  24. throw new ResourceAccessException( "I/O error on " + method.name() +
  25. " request for \"" + resource + "\": " + ex.getMessage(), ex);
  26. }
  27. finally {
  28. if (response != null) {
  29. response.close();
  30. }
  31. }
  32. }

其中涉及到多个方法:

createRequest(url, method)

此处调用的是HttpAccessor中的createRequest方法


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

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


   
  1. public ClientHttpRequestFactory getRequestFactory () {
  2. List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
  3. if (!CollectionUtils.isEmpty(interceptors)) {
  4. ClientHttpRequestFactory factory = this.interceptingRequestFactory;
  5. if (factory == null) {
  6. factory = new InterceptingClientHttpRequestFactory( super.getRequestFactory(), interceptors);
  7. this.interceptingRequestFactory = factory;
  8. }
  9. return factory;
  10. }
  11. else {
  12. return super.getRequestFactory();
  13. }
  14. }

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


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

而HttpAccessor中使用的是SimpleClientHttpRequestFactory类。

接着走进SimpleClientHttpRequestFactory的createRequest方法


   
  1. public ClientHttpRequest createRequest (URI uri, HttpMethod httpMethod) throws IOException {
  2. //打开连接
  3. HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
  4. //前期准备工作
  5. prepareConnection(connection, httpMethod.name());
  6. //默认为true
  7. if ( this.bufferRequestBody) {
  8. return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
  9. }
  10. else {
  11. return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
  12. }
  13. }

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


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

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

prepareConnection(connection, httpMethod.name())


   
  1. protected void prepareConnection (HttpURLConnection connection, String httpMethod) throws IOException {
  2. //设置连接超时时间
  3. if ( this.connectTimeout >= 0) {
  4. connection.setConnectTimeout( this.connectTimeout);
  5. }
  6. //设置读取超时时间
  7. if ( this.readTimeout >= 0) {
  8. connection.setReadTimeout( this.readTimeout);
  9. }
  10. //设置请求方法等
  11. boolean mayWrite =
  12. ( "POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
  13. "PATCH".equals(httpMethod) || "DELETE".equals(httpMethod));
  14. connection.setDoInput( true);
  15. connection.setInstanceFollowRedirects( "GET".equals(httpMethod));
  16. connection.setDoOutput(mayWrite);
  17. connection.setRequestMethod(httpMethod);
  18. }

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

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

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


   
  1. protected <T> T doExecute (URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
  2. @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
  3. Assert.notNull(url, "URI is required");
  4. Assert.notNull(method, "HttpMethod is required");
  5. ClientHttpResponse response = null;
  6. try {
  7. //创建文章开头所说的ClientHttpRequest
  8. ClientHttpRequest request = createRequest(url, method);
  9. if (requestCallback != null) {
  10. //执行请求回调
  11. requestCallback.doWithRequest(request);
  12. }
  13. //执行请求,U获取响应结果
  14. response = request.execute();
  15. //处理响应结果
  16. handleResponse(url, method, response);
  17. //利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
  18. return (responseExtractor != null ? responseExtractor.extractData(response) : null);
  19. }
  20. catch (IOException ex) {
  21. String resource = url.toString();
  22. String query = url.getRawQuery();
  23. resource = (query != null ? resource.substring( 0, resource.indexOf( '?')) : resource);
  24. throw new ResourceAccessException( "I/O error on " + method.name() +
  25. " request for \"" + resource + "\": " + ex.getMessage(), ex);
  26. }
  27. finally {
  28. if (response != null) {
  29. response.close();
  30. }
  31. }
  32. }

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


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

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

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

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

 execute方法在AbstractClientHttpRequest类中


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

进入executeInternal方法中,位于AbstractBufferingClientHttpRequest中


   
  1. protected ClientHttpResponse executeInternal (HttpHeaders headers) throws IOException {
  2. byte[] bytes = this.bufferedOutput.toByteArray();
  3. if (headers.getContentLength() < 0) {
  4. headers.setContentLength(bytes.length);
  5. }
  6. ClientHttpResponse result = executeInternal(headers, bytes);
  7. this.bufferedOutput = new ByteArrayOutputStream( 0);
  8. return result;
  9. }

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


   
  1. protected ClientHttpResponse executeInternal (HttpHeaders headers, byte[] bufferedOutput) throws IOException {
  2. //将请求头设置进connection中
  3. addHeaders( this.connection, headers);
  4. // JDK <1.8 doesn't support getOutputStream with HTTP DELETE
  5. if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
  6. this.connection.setDoOutput( false);
  7. }
  8. if ( this.connection.getDoOutput() && this.outputStreaming) {
  9. this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
  10. }
  11. this.connection.connect();
  12. if ( this.connection.getDoOutput()) {
  13. //将缓存区的数据复制进connection的输出流中
  14. FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
  15. }
  16. else {
  17. // Immediately trigger the request in a no-output scenario as well
  18. this.connection.getResponseCode();
  19. }
  20. return new SimpleClientHttpResponse( this.connection);
  21. }

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

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

再贴一遍主流程


   
  1. protected <T> T doExecute (URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
  2. @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
  3. Assert.notNull(url, "URI is required");
  4. Assert.notNull(method, "HttpMethod is required");
  5. ClientHttpResponse response = null;
  6. try {
  7. //创建文章开头所说的ClientHttpRequest
  8. ClientHttpRequest request = createRequest(url, method);
  9. if (requestCallback != null) {
  10. //执行请求回调
  11. requestCallback.doWithRequest(request);
  12. }
  13. //执行请求,获取响应结果
  14. response = request.execute();
  15. //处理响应结果
  16. handleResponse(url, method, response);
  17. //利用响应抽取器抽取data返回预先定义的java对象,例如例子中的String
  18. return (responseExtractor != null ? responseExtractor.extractData(response) : null);
  19. }
  20. catch (IOException ex) {
  21. String resource = url.toString();
  22. String query = url.getRawQuery();
  23. resource = (query != null ? resource.substring( 0, resource.indexOf( '?')) : resource);
  24. throw new ResourceAccessException( "I/O error on " + method.name() +
  25. " request for \"" + resource + "\": " + ex.getMessage(), ex);
  26. }
  27. finally {
  28. if (response != null) {
  29. response.close();
  30. }
  31. }
  32. }

handleResponse(url, method, response)


   
  1. protected void handleResponse (URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
  2. //获取错误处理器
  3. ResponseErrorHandler errorHandler = getErrorHandler();
  4. boolean hasError = errorHandler.hasError(response);
  5. if (logger.isDebugEnabled()) {
  6. try {
  7. int code = response.getRawStatusCode();
  8. HttpStatus status = HttpStatus.resolve(code);
  9. logger.debug( "Response " + (status != null ? status : code));
  10. }
  11. catch (IOException ex) {
  12. // ignore
  13. }
  14. }
  15. //如果出现错误的话,例如状态码为500,则进入到错误处理流程中
  16. if (hasError) {
  17. errorHandler.handleError(url, method, response);
  18. }
  19. }

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

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

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

responseExtractor是HttpMessageConverterExtractor类型的


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

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

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

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

一句话概括RestTemplate,

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值