我们知道你要是想发起一个请求,需要指定两个环节内容,一个是请求内容对象(request),一个是连接内容对象(httpClient)
它们两个的作用我们在下面会看到
1.先说一下结论,spring所有的核心代码都在doxxx()方法里面,而http请求的核心代码在doExecute()中。
# 我们平时会写这个一个方法去开启http请求调用
restTemplate.postForObject(url,httpEntity, xxx.class);
# 往里钻
@Nullable
public <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()方法
@Nullable
public <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的操作
@Nullable
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;
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来覆盖原有父类factory
factory = 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;
@Nullable
private RequestConfig requestConfig;
private boolean bufferRequestBody = true;
@Nullable
private BiFunction<HttpMethod, URI, HttpContext> httpContextFactory;
}
#interceptor
public interface ClientHttpRequestInterceptor {
// 重点关注!!!request对象
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;
}
我们可以发现在factory中我们可以对连接对象进行修改,在interceptor中可以对请求对象进行修改;我们回归下文章开头,一个请求的两个组成部分我们已经发现了。接下来就演示下不同场景应该怎么使用这两种对象。
分析:请求头内容属于请求对象,所以我们通过interceptor来实现
@Configuration
public class RestTemplateConfig {
/**
* restTemplate
*/
@ConditionalOnMissingBean
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate();
ClientHttpRequestInterceptor clientHttpRequestInterceptor = new ClientHttpRequestInterceptor() {
@Override
public 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) {
// 修改factory
RestTemplate restTemplate = new RestTemplate(generateHttpRequestFactory());
ClientHttpRequestInterceptor clientHttpRequestInterceptor = new ClientHttpRequestInterceptor() {
@Override
public 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);
}
};
// 修改interceptor
restTemplate.setInterceptors(Collections.singletonList(clientHttpRequestInterceptor));
return restTemplate;
}