首先明确两个事情:请求对象,连接对象
我们知道你要是想发起一个请求,需要指定两个环节内容,一个是请求内容对象(request),一个是连接内容对象(httpClient)
它们两个的作用我们在下面会看到
简要分析源码
1.先说一下结论,spring所有的核心代码都在doxxx()方法里面,而http请求的核心代码在doExecute()中。
# 我们平时会写这个一个方法去开启http请求调用
restTemplate.postForObject(url,httpEntity, xxx.class);# 往里钻
@Nullablepublic <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {RequestCallback requestCallback = this.httpEntityCallback(request, responseType);HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor(responseType, this.getMessageConverters(), this.logger);return this.execute(url, HttpMethod.POST, requestCallback, responseExtractor, (Object[])uriVariables);}#继续钻,发现了doxxx()方法
@Nullablepublic <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {URI expanded = this.getUriTemplateHandler().expand(url, uriVariables);return this.doExecute(expanded, method, requestCallback, responseExtractor);}#看看实现,我们会发现有一个创建request的操作
@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;Object var14;try {# 核心!!!ClientHttpRequest request = this.createRequest(url, method);if (requestCallback != null) {requestCallback.doWithRequest(request);}response = request.execute();this.handleResponse(url, method, response);var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;} catch (IOException var12) {String resource = url.toString();String query = url.getRawQuery();resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12);} finally {if (response != null) {response.close();}}return var14;}# 我们发现所有的request对象都是通过factory创建的,不同的factory会创建不同的request对象
# 因为目前我们位于抽象类HttpAccessor中,所以我们要继续往实现类追踪getRequestFactory()方法
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {ClientHttpRequest request = this.getRequestFactory().createRequest(url, method);this.initialize(request);if (this.logger.isDebugEnabled()) {this.logger.debug("HTTP " + method.name() + " " + url);}return request;}# 此时我们位于InterceptingHttpAccessor抽象类中,继续往下追踪就是RestInterceptors类了,没有重写getRequestFactory()方法,所以我们要重点关注InterceptingHttpAccessor抽象类中的重写逻辑:
public ClientHttpRequestFactory getRequestFactory() {List<ClientHttpRequestInterceptor> interceptors = this.getInterceptors();if (!CollectionUtils.isEmpty(interceptors)) {ClientHttpRequestFactory factory = this.interceptingRequestFactory;if (factory == null) {// 如果有interceptors,则融合父类的factory和interceptors,返回一个新的factory来覆盖原有父类factoryfactory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);this.interceptingRequestFactory = (ClientHttpRequestFactory)factory;}return (ClientHttpRequestFactory)factory;} else {// 如果没有则interceptors,则直接用父类中的factory,return super.getRequestFactory();}}
至此,我们可以得到以下结论,如果想对原有请求进行扩展,我们需要从两个对象进行下手:factory,interceptor。
接下来我们看一下factory和interceptor两个类中都有什么内容:
#factory
public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {// 重点关注!!!连接对象private HttpClient httpClient;@Nullableprivate RequestConfig requestConfig;private boolean bufferRequestBody = true;@Nullableprivate BiFunction<HttpMethod, URI, HttpContext> httpContextFactory;
}#interceptor
public interface ClientHttpRequestInterceptor {// 重点关注!!!request对象ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;
}
我们可以发现在factory中我们可以对连接对象进行修改,在interceptor中可以对请求对象进行修改;我们回归下文章开头,一个请求的两个组成部分我们已经发现了。接下来就演示下不同场景应该怎么使用这两种对象。
场景1:添加固定请求头
分析:请求头内容属于请求对象,所以我们通过interceptor来实现
@Configuration
public class RestTemplateConfig {/*** restTemplate*/@ConditionalOnMissingBean@Beanpublic RestTemplate restTemplate(ClientHttpRequestFactory factory) {RestTemplate restTemplate = new RestTemplate();ClientHttpRequestInterceptor clientHttpRequestInterceptor = new ClientHttpRequestInterceptor() {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)throws IOException {request.getHeaders().set("X-ID", "");request.getHeaders().set("X-APPKEY", "");return execution.execute(request, body);}};restTemplate.setInterceptors(Collections.singletonList(clientHttpRequestInterceptor));return restTemplate;}
}
场景二:添加请求证书
分析:请求头内容属于连接对象,所以我们通过factory来实现
public static HttpComponentsClientHttpRequestFactory generateHttpRequestFactory() {TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;SSLContext sslContext = null;try {sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)// 增加请求证书.loadKeyMaterial(ks, keyStorePassword.toCharArray()).setProtocol("TLSv1.2").build();} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {log.error("generateHttpRequestFactory failed:", e);}SSLConnectionSocketFactory connectionSocketFactory =new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());HttpClientBuilder httpClientBuilder = HttpClients.custom();httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);CloseableHttpClient httpClient = httpClientBuilder.build();HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();factory.setHttpClient(httpClient);factory.setConnectTimeout(15000);factory.setReadTimeout(5000);return factory;
}
当然两者可以同时存在:
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {// 修改factoryRestTemplate restTemplate = new RestTemplate(generateHttpRequestFactory());ClientHttpRequestInterceptor clientHttpRequestInterceptor = new ClientHttpRequestInterceptor() {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)throws IOException {request.getHeaders().set("X-HW-ID", "com.huawei.osec");request.getHeaders().set("X-HW-APPKEY", "/D2QodV7Lu2EUk4D9HEUsQ==");return execution.execute(request, body);}};// 修改interceptorrestTemplate.setInterceptors(Collections.singletonList(clientHttpRequestInterceptor));return restTemplate;}