项目中几乎所有的rpc调用都用了RestTemplate,日志并不完善, 同事要对所有请求增加一个日志和响应的日志输出
选用了Interceptor进行RestTemplate的增强,碰到了流只能用一次的问题. 网上有两种解决的方向:
因此决定撸一下源码给一个有理有据的解决方案
@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
//...
try {
//核心是createRequest构建的request中包含什么逻辑
ClientHttpRequest request = createRequest(url, method);
//执行的代码在request.execute()
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
//...
}
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
}
RestTemplate继承的是该类.
@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();
}
}
requestTemplate进行远程调用时, 会走到InterceptingClientHttpRequest.execute方法,内部再链到executeInternal上
class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {
private final ClientHttpRequestFactory requestFactory;
private final List<ClientHttpRequestInterceptor> interceptors;
//父类中执行execute时,会调用到子类的executeInternal方法
@Override
protected 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();
}
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
//...
return delegate.execute();
}
}
}
如果需要对响应流进行包装实现可重用. 最佳的位置有两处: 最后一个interceptor或者delegate本身返回的就是可重用流
这里就想到了网上提到的 BufferingClientHttpRequestFactory , 这是源码自身提供的支持, 只不过由于这个功能应该不是通用的功能但是会多占用内存, 因此提供支持, 但是是否用就看开发者本身了
public ClientHttpRequestFactory buildRequestFactory() {
ClientHttpRequestFactory requestFactory = null;
if (this.requestFactory != null) {
requestFactory = this.requestFactory.get();
}
else if (this.detectRequestFactory) {
requestFactory = new ClientHttpRequestFactorySupplier().get();
}
if (requestFactory != null) {
if (this.requestFactoryCustomizer != null) {
this.requestFactoryCustomizer.accept(requestFactory);
}
}
return requestFactory;
}
public class ClientHttpRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {
private static final Map<String, String> REQUEST_FACTORY_CANDIDATES;
static {
Map<String, String> candidates = new LinkedHashMap<>();
candidates.put("org.apache.http.client.HttpClient",
"org.springframework.http.client.HttpComponentsClientHttpRequestFactory");
candidates.put("okhttp3.OkHttpClient", "org.springframework.http.client.OkHttp3ClientHttpRequestFactory");
REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates);
}
//根据引入的依赖决定加载的是什么工厂
@Override
public ClientHttpRequestFactory get() {
for (Map.Entry<String, String> candidate : REQUEST_FACTORY_CANDIDATES.entrySet()) {
ClassLoader classLoader = getClass().getClassLoader();
if (ClassUtils.isPresent(candidate.getKey(), classLoader)) {
Class<?> factoryClass = ClassUtils.resolveClassName(candidate.getValue(), classLoader);
return (ClientHttpRequestFactory) BeanUtils.instantiateClass(factoryClass);
}
}
return new SimpleClientHttpRequestFactory();
}
}
@Test
public void test() {
RestTemplate restTemplate = new RestTemplate();
ClientHttpRequestFactory clientHttpRequestFactory = new ClientHttpRequestFactorySupplier().get();
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(clientHttpRequestFactory));
restTemplate.getInterceptors().add(new LoggingInterceptor());
String forObject = restTemplate.getForObject("http://www.baidu.com", String.class);
System.out.println("forObject = " + forObject);
}
//这时候所有的拦截器都可以将body获取到随便用了
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse execute = execution.execute(request, body);
System.out.println("body = " + new String(execute.getBody().readAllBytes()));
System.out.println("body = " + new String(execute.getBody().readAllBytes()));
return execute;
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。