当前位置: 代码迷 >> 综合 >> Ribbon(1)--->RestTemplate使用与原理分析
  详细解决方案

Ribbon(1)--->RestTemplate使用与原理分析

热度:82   发布时间:2023-10-24 14:57:18.0

1、RestTemplate 是什么?有何作用?使用场景?

      RestTemplate 是spring-web 对rest规范的一个封装;它的作用就是:可以发起rest规范的请求;使用场景:凡是你在代码中需要发起http请求的地方都能使用。

2、RestTemplate的使用案例:

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<OrderDto> forEntity = restTemplate.getForEntity("http://localhost:8080/getOrderById?orderId=" + orderId, OrderDto.class);

  这是一个简单的get请求的案例,RestTemplate里面封装了rest规范的所有发起请求的方式,此处我们不在一 一列举,因为我们不是偏向使用,我们主要是来分析RestTemplate的原理的。

 

3、废话不多说,源码分析开始:

     3.1、先看类图关系:

                                    

                         实现父类如下:  

                                   RestOperations : 顾名思义,这个接口就是定义了rest规范的操作集。

                                   HttpAccessor:http存储器,这个听起来是不是很奇怪,之所以叫存储器,那是因为HttpAccessor这个类能够创建Http请求实例ClientHttpRequest出来,所以就叫Http存储器。     

                                   InterceptingHttpAccessor : 拦截器存储器,里面存放的是http请求的拦截器列表。

 

      3.2、HttpAccessorhttp存储器源码分析:作用使用其成员属性ClientHttpRequestFactory requestFactory创建客户端http请求实例ClientHttpRequest

public abstract class HttpAccessor {protected final Log logger = HttpLogging.forLogName(getClass());客户端请求工厂,用于创建客户端http请求实例ClientHttpRequest,默认是SimpleClientHttpRequestFactory,可以进行替换,例如切换为apache httpclient、okhttp都可以。private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();客户端http请求初始者列表,用于初始化创建好的客户端http请求。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;}创建客户端http请求且使用初始化者进行初始化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));}}

             ClientHttpRequestFactory接口类图:

                     

                     在上面类图中我们没有对RibbonClientHttpRequestFactory进行说明,原因是RibbonClientHttpRequestFactory 这个客户端请求工厂它是使用了Netflix的RestClient来进行http请求的发起与响应的处理的,其底层处理http请求与响应的http客户端是jersey。

                     InterceptingClientHttpRequestFactory只是一个包装了ClientHttpRequestFactory + interceptors的包装器。

                     我们知道了ClientHttpRequestFactory是用来创建客户端http请求实例的ClientHttpRequest,其实ClientHttpRequest也是一个接口,接下来我们看看ClientHttpRequest的接口类图:

                                   

                       每一种ClientHttpRequestFactory创建的客户端请求实例类型关系:

                                   SimpleClientHttpRequestFactory------------> SimpleBufferingClientHttpRequest(HttpURLConnection connection, boolean outputStreaming)

                                   HttpComponentsClientHttpRequestFactory--------> HttpComponentsClientHttpRequest(HttpClient client, HttpUriRequest request, HttpContext context)

                                   OkHttp3ClientHttpRequestFactory--------------> OkHttp3ClientHttpRequest(OkHttpClient client, URI uri, HttpMethod method)

                                   RibbonClientHttpRequestFactory---------> RibbonHttpRequest(URI uri, HttpRequest.Verb verb, RestClient client, IClientConfig config)

                                   InterceptingClientHttpRequestFactory--------> InterceptingClientHttpRequest(ClientHttpRequestFactory chrf, List<ClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod method)

 

 

     3.3、InterceptingHttpAccessor拦截器储存器源码分析

public abstract class InterceptingHttpAccessor extends HttpAccessor {储存的拦截器,这些拦截器会在http请求被执行之前进行调用private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();看见没这里立马就使用了有拦截功能的请求工厂,就是我们上面提到的InterceptingClientHttpRequestFactory@Nullableprivate volatile ClientHttpRequestFactory interceptingRequestFactory;public级别的方法,因此可以设置拦截器列表public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {Assert.noNullElements(interceptors, "'interceptors' must not contain null elements");// Take getInterceptors() List as-is when passed in hereif (this.interceptors != interceptors) {this.interceptors.clear();this.interceptors.addAll(interceptors);AnnotationAwareOrderComparator.sort(this.interceptors);}}public List<ClientHttpRequestInterceptor> getInterceptors() {return this.interceptors;}设置客户端http请求工厂,这里我们就能够替换掉RestTemplate底层的httpclient的类型,例如切换成okhttp3.@Overridepublic void setRequestFactory(ClientHttpRequestFactory requestFactory) {调用父类的setRequestFactory方法,父类是谁,不就是HttpAccessor吗,那这样的话HttpAccessor中的requestFactory = 我们设置的了,不再是默认的new SimpleClientHttpRequestFactory()了吧,这就起到了切换httpclient类型的效果。super.setRequestFactory(requestFactory);this.interceptingRequestFactory = null;}获取请求工厂,这个是核心方法!!!!!!!!!!!!!!!!@Overridepublic ClientHttpRequestFactory getRequestFactory() {1、先获取存储的拦截器列表List<ClientHttpRequestInterceptor> interceptors = getInterceptors();2、如果有拦截器那就构建一个带拦截功能的客户端http请求工厂InterceptingClientHttpRequestFactory实例返回。if (!CollectionUtils.isEmpty(interceptors)) {ClientHttpRequestFactory factory = this.interceptingRequestFactory;if (factory == null) {factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);this.interceptingRequestFactory = factory;}return factory;}3、如果没有拦截器那就直接获取父类HttpAccessor中的客户端http请求工厂实例返回。else {return super.getRequestFactory();}}}

 

     3.4、最终的实现类RestTemplate源码解析:

              上面我们把 RestTemplate 的父类 InterceptingHttpAccessor 以及 InterceptingHttpAccessor 的父类 HttpAccessor 进行了剖析,下面我们来剖析最终的实现类 RestTemplate;

               3.4.1、RestTemplate 的核心属性:

                        Ⅰ:    List<HttpMessageConverter<?>> messageConverters:消息转换器列表,在 springmvc 中这个应该不陌生。

                        Ⅱ: ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); :响应错误处理器。

                        Ⅲ: ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor(); :响应头提取器。

                        Ⅳ:继承父类 HttpAccessor 的属性 ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory() :http 请求构建工厂。

                       Ⅴ:继承父类 InterceptingHttpAccessor 的属性private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>() :拦截器列表,这个是很重要的东西,在spring-cloud-netflix-ribbon中就是使用了拦截器进行负载均衡处理的。

 

                3.4.2、RestTemplate 核心属性的设置方式:

                         方式1:set函数:

             restTemplate.setErrorHandler(ResponseErrorHandler errorHandler);restTemplate.setMessageConverters(List<HttpMessageConverter<?>> messageConverters);restTemplate.setRequestFactory(ClientHttpRequestFactory requestFactory);restTemplate.setInterceptors(List<ClientHttpRequestInterceptor> interceptors);

                        方式2: 构造函数, 默认的无参数构造函数会自动设置一些常用的 HttpMessageConverter 列表;

             这个构造函数可以设置ClientHttpRequestFactory属性,因此可以设置为okhttp、apache httpComponentClient等客户端。public RestTemplate(ClientHttpRequestFactory requestFactory) {this();setRequestFactory(requestFactory);}这个构造函数可以设置入参HttpMessageConverter列表,拦截器的顺序可以使用@Order注解,或者实现Ordered接口来进行定义。public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {validateConverters(messageConverters);this.messageConverters.addAll(messageConverters);this.uriTemplateHandler = initUriTemplateHandler();}

 

             3.4.3、RestTemplate执行流程的解析;

                         案例1:无拦截器案例

                RestTemplate restTemplate = new RestTemplate();ResponseEntity<OrderDto> forEntity = restTemplate.getForEntity("http://localhost:8080/getOrderById?orderId=" + orderId, OrderDto.class);

                                 第1步:RestTemplate.getForEntity(String url, Class<T> responseType, Object... uriVariables);

	                   @Overridepublic <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)throws RestClientException {RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));}

                                  第2步:RestTemplate.execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables);

	                  @Override@Nullablepublic <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {获取url模板处理器进行url的处理,例如GET请求的参数替换。URI expanded = getUriTemplateHandler().expand(url, uriVariables);return doExecute(expanded, method, requestCallback, responseExtractor);}

                                   第3步:RestTemplate.doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor);

                       @Nullableprotected <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、创建一个请求,此处就会调用父类HttpAccessor的createRequest方法。ClientHttpRequest request = createRequest(url, method);if (requestCallback != null) {requestCallback.doWithRequest(request);}2、执行请求。response = request.execute();3、处理响应handleResponse(url, method, response);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的createRequest(URI url, HttpMethod method)实现:protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {先获取客户端http请求工厂,然后使用请求工厂创建客户端http请求实例,
在我们当前的案例中requestFactory = new SimpleClientHttpRequestFactory()的,因此创建的客户端
请求实例就是SimpleBufferingClientHttpRequest(connection, this.outputStreaming)实例,jdk的
HttpURLConnection来进行请求发送,在创建SimpleClientHttpRequestFactory的createRequest方法中会
先openConnection也就是打开连接,然后使用连接构建SimpleBufferingClientHttpRequest返回。ClientHttpRequest request = getRequestFactory().createRequest(url, method);初始化客户端http请求实例initialize(request);if (logger.isDebugEnabled()) {logger.debug("HTTP " + method.name() + " " + url);}返回客户端http请求实例return request;}SimpleBufferingClientHttpRequest的execute()方法实现:首先执行一级父类AbstractClientHttpRequest的execute()方法:@Overridepublic final ClientHttpResponse execute() throws IOException {assertNotExecuted();执行内部的执行函数ClientHttpResponse result = executeInternal(this.headers);this.executed = true;return result;}接着来到二级父类AbstractBufferingClientHttpRequestexecuteInternal(..):@Overrideprotected 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(...):protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {addHeaders(this.connection, headers);// JDK <1.8 doesn't support getOutputStream with HTTP DELETEif (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {this.connection.setDoOutput(false);}if (this.connection.getDoOutput() && this.outputStreaming) {this.connection.setFixedLengthStreamingMode(bufferedOutput.length);}发起http请求this.connection.connect();if (this.connection.getDoOutput()) {FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());}else {// Immediately trigger the request in a no-output scenario as wellthis.connection.getResponseCode();}return new SimpleClientHttpResponse(this.connection);}

                                                

 

                         案例2:有拦截器案例

           @Order(0)@Componentpublic class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {System.out.println("call MyClientHttpRequestInterceptor intercept . . .");execution.execute(request, body);return null;}}@Beanpublic RestTemplate restTemplate(MyClientHttpRequestInterceptor myClientHttpRequestInterceptor){RestTemplate restTemplate = new RestTemplate();List<ClientHttpRequestInterceptor> intercepters = new ArrayList <>();intercepters.add(myClientHttpRequestInterceptor);restTemplate.setInterceptors(intercepters);return restTemplate;}@Autowiredprivate RestTemplate restTemplate;@GetMapping("userGetOrderById")public OrderDto userGetOrderById(@RequestParam("orderId") String orderId){ResponseEntity<OrderDto> forEntity = restTemplate.getForEntity("http://localhost:8080/getOrderById?orderId=" + orderId, OrderDto.class);return forEntity.getBody();}

                     跟没有拦截器的唯一的区别就在于RestTemplate.doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor)这个方法中的依据代码:ClientHttpRequest request = createRequest(url, method); 就会调用父类HttpAccessor的createRequest(URI url, HttpMethod method)方法,源码如下:

	         protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {在当前的HttpAccessor类中有一个getRequestFactory(),然而在子类
InterceptingHttpAccessor中复写了这个方法,那么就会来到子类InterceptingHttpAccessor中ClientHttpRequest request = getRequestFactory().createRequest(url, method);initialize(request);if (logger.isDebugEnabled()) {logger.debug("HTTP " + method.name() + " " + url);}return request;}子类InterceptingHttpAccessor中的 getRequestFactory()实现如下:@Overridepublic ClientHttpRequestFactory getRequestFactory() {List<ClientHttpRequestInterceptor> interceptors = getInterceptors();如果存在拦截器,那就构建一个InterceptingClientHttpRequestFactory类型的客户端请求工厂,我们都知道InterceptingClientHttpRequestFactory只是一个包装,因此会使用父类HttpAccessor的requestFactory来构建InterceptingClientHttpRequestFactory实例。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();}}

                         我们在上面提及到过InterceptingClientHttpRequestFactory创建的请求类型是InterceptingClientHttpRequest,InterceptingClientHttpRequestFactory源码如下:

                 public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {private final List<ClientHttpRequestInterceptor> interceptors;public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,@Nullable List<ClientHttpRequestInterceptor> interceptors) {super(requestFactory);this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());}创建InterceptingClientHttpRequest请求实例@Overrideprotected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);}}

                         InterceptingClientHttpRequest请求源码如下:

                class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {private final ClientHttpRequestFactory requestFactory;private final List<ClientHttpRequestInterceptor> interceptors;private HttpMethod method;private URI uri;protected InterceptingClientHttpRequest(ClientHttpRequestFactory requestFactory,List<ClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod method) {this.requestFactory = requestFactory;this.interceptors = interceptors;this.method = method;this.uri = uri;}@Overridepublic HttpMethod getMethod() {return this.method;}@Overridepublic String getMethodValue() {return this.method.name();}@Overridepublic URI getURI() {return this.uri;}最终执行父类的execute后会调用到此处的executeInternal方法@Overrideprotected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {创建一个拦截器请求执行器实例,拦截器请求执行器类是当前类的内部类InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();使用拦截器请求执行器实例执行当前请求return requestExecution.execute(this, bufferedOutput);}拦截器请求执行器类private class InterceptingRequestExecution implements ClientHttpRequestExecution {private final Iterator<ClientHttpRequestInterceptor> iterator;public InterceptingRequestExecution() {this.iterator = interceptors.iterator();}@Overridepublic ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {来到拦截器请求执行器类的执行方法,第一步就是循环执行所有的拦截器if (this.iterator.hasNext()) {ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();return nextInterceptor.intercept(request, body, this);}所有的拦截器执行完成后开始执行http请求的发起与处理响应else {HttpMethod method = request.getMethod();Assert.state(method != null, "No standard HTTP method");当前的拦截器请求实例InterceptingClientHttpRequest只是一个包装,因此还需要使用HttpAccessor的requestFactory来创建真正的客户端http请求实例。ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));if (body.length > 0) {if (delegate instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));}else {StreamUtils.copy(body, delegate.getBody());}}使用真正的客户端http请求实例发起http请求。return delegate.execute();}}}}

 

以上就是RestTemplate的原理剖析,之所以来剖析RestTemplate的原理,就是为了后面学习spring-cloud-netfliux-ribbon打基础。

 

 

  相关解决方案