InterceptingHttpAccessor
// @since 3.0 // @see InterceptingClientHttpRequestFactory public abstract class InterceptingHttpAccessor extends HttpAccessor { // 装载需要作用在RestTemplate上的拦截器们~~~ private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); @Nullable private volatile ClientHttpRequestFactory interceptingRequestFactory; // 这里语意是set,所以是完全的替换掉(支持ordered排序哦~~~) public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) { if (this.interceptors != interceptors) { this.interceptors.clear(); this.interceptors.addAll(interceptors); AnnotationAwareOrderComparator.sort(this.interceptors); } } // 复写了父类的这个方法很有意思 // 意思为:若你调用者手动set进来了,那就以调用者设置的工厂为准 否则使用的是InterceptingClientHttpRequestFactory @Override public void setRequestFactory(ClientHttpRequestFactory requestFactory) { super.setRequestFactory(requestFactory); this.interceptingRequestFactory = null; } // 若配置了拦截器,那么默认就使用InterceptingClientHttpRequestFactory,而不再是SimpleClientHttpRequestFactory了~~~ @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(); } } }
InterceptingHttpAccessor最主要的处理逻辑为:若发现调用者设置了请求拦截器,那么它创建的工厂是具有拦截功能的InterceptingClientHttpRequestFactory,否则就是默认的SimpleClientHttpRequestFactory。
InterceptingClientHttpRequestFactory工厂它产生的ClientHttpRequest是InterceptingClientHttpRequest,然而它就会执行拦截器的拦截方法喽:nextInterceptor.intercept(request, body, this)
提问:如有配置有多个请求拦截器,都会执行吗?
解答:这个千万不要犯迷糊和轻易下结论:以为没有迭代它(for循环)而只是iterator.next()就以为若有多个就只会执行一个,那就大错特错了。这里实际是形成了一个执行链条,只要拦截器的intercept方法内最终还调用执行器的intercept()方法,那么拦截器链就会一直执行下去。其根本缘由是第三个参数传入的是this,至始至终都是同一个执行器(this=InterceptingRequestExecution)
RestTemplate
RestTemplate采用同步方式执行 HTTP 请求的类,底层默认使用JDK原生 HttpURLConnection API。它实现了接口RestOperations,提供了非常多的模版方法(重载方法)让开发者能更简单地发送 HTTP 请求。
需要注意的是,RestTemplate是Spring 3.0就有了,但在Spring5.0后,Spring官方是推荐使用org.springframework.web.reactive.function.client.WebClient替代它,特别是对于异步的场景。
RestTemplate因为使用极其广泛,so即使到了Spring 5.0,官方只是建议替代,但并没有标注@Deprecated,因此至少目前你还可以想咋用就咋用吧。
但是AsyncRestTemplate是明确标注了@Deprecated,强烈建议使用org.springframework.web.reactive.function.client.WebClient去代替,所以在5.0后不建议再使用它了~。
当然还需要说明一点:若你的项目中没有使用到WebFlux的技术栈来处理请求,那么也没必要说为了使用而使用,所以没必要专门为了它而导包(个人建议)~
// @since 3.0 public class RestTemplate extends InterceptingHttpAccessor implements RestOperations { // 去classpath探测 是否有这些消息转换器相关的jar~ // 一般情况下我们都会导jackson2Present~~~ private static boolean romePresent; private static final boolean jaxb2Present; private static final boolean jackson2Present; private static final boolean jackson2XmlPresent; private static final boolean jackson2SmilePresent; private static final boolean jackson2CborPresent; private static final boolean gsonPresent; private static final boolean jsonbPresent; ... // 下面四个变量很重要: // 消息转换器们(显然对JSON格式默认是支持得最好的) private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); // 默认的请求异常处理器,Spring5.0后其实可以使用它ExtractingResponseErrorHandler // 它能够利用消息换换气提取你的错误内容。并且还支持自定义错误码、错误序列等等~ private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); // 用于URL的构建 private UriTemplateHandler uriTemplateHandler; // 默认的返回值提取器~~~~ private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor(); // 空构造,应该是平时使用得最多的了:一切都使用默认的组件配置Resource等等 public RestTemplate() { // 这个几个消息转换器是支持的。字节数组、字符串、 this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); this.messageConverters.add(new SourceHttpMessageConverter<>()); // 对form表单提交方式的支持 this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); // 接下里便是一些列的判断,若类路径上有才会加进来 if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } ... // new DefaultUriBuilderFactory() this.uriTemplateHandler = initUriTemplateHandler(); } // 你懂的,若想用OkHttp,也可以在构造时就指定 public RestTemplate(ClientHttpRequestFactory requestFactory) { this(); setRequestFactory(requestFactory); } // 若不想用默认的消息转换器,也可以自己指定(其实一般都不这么去干,而是后面自己再add进来) public RestTemplate(List<HttpMessageConverter<?>> messageConverters) { Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); this.messageConverters.addAll(messageConverters); this.uriTemplateHandler = initUriTemplateHandler(); } ... // 省略上面属性的get/set犯法们 }
这部分源码我列出来,都是在对构建一个RestTemplate
实例的准备工作相关方法,包括对各个相关组件的设置。
接下来更重要的便是它实现的接口方法了,我抽出一些关键点进行描述说明:
RestTemplate: @Override @Nullable public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { //1、new AcceptHeaderRequestCallback(responseType) 它能在发送请求的之前这样一件事: // request.getHeaders().setAccept(allSupportedMediaTypes) RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); // 最终调用的是execute方法,此时URL是个字符串 // responseExtractor返回值提取器使用的是消息转换器去读取body哒~ // 返回值就是返回的body本身(不含有返回的响应头等等信息~) return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } // 它返回的是ResponseEntity,不会返回null的 最终调用的依旧是execute方法 // 此时候用的就不是消息转换器的提取器了,而是内部类`ResponseEntityResponseExtractor`(底层还是依赖消息转换器) // 但是这个提取器,提取出来的可都是ResponseEntity<T>实例~ @Override public <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)); } // HEAD请求:很简单,使用的提取器就是headersExtractor,从返回值里把响应header拿出来即可 @Override public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException { return nonNull(execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables)); } // POST请求 @Override @Nullable public URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException { // 1、HttpEntityRequestCallback 适配:把request适配成一个HttpEntity // 然后执行前,通过消息转换器把头信息、body信息等等都write进去 RequestCallback requestCallback = httpEntityCallback(request); // 因为需要拿到URI,所以此处使用headersExtractor提取器先拿到响应的header即可~~~ HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return (headers != null ? headers.getLocation() : null); } // 除了httpEntityCallback()不一样,其余和get请求一样 @Override @Nullable public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger); return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); } // PUT请求:因为没有返回值,所以不需要返回值提取器。所以,非常的简单~~~ @Override public void put(String url, @Nullable Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } // DELETE请求:也是木有返回值的。 // 并且请注意:DELETE请求这里可都是不能接收body的,不能给请求设置请求体的 // (虽然可能底层httpCLient支持,但这里不支持,请遵守规范) @Override public void delete(String url, Object... uriVariables) throws RestClientException { execute(url, HttpMethod.DELETE, null, null, uriVariables); } // OPTIONS请求:和HEAD请求的处理逻辑几乎一样 @Override public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException { ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return (headers != null ? headers.getAllow() : Collections.emptySet()); }