【Spring】之 RestTemplate淺析

版本:Spring 5


一、源碼解讀


先從創建開始
RestTemplate restTemplate = new RestTemplate();

來看下 RestTemplate的類圖:
在這裏插入圖片描述

可知,當初始化RestTemplate時候,同時會先生成HttpAccessorInterceptingHttpAccessorRestOperations

那麼來看下這些父類和接口:


(1)HttpAccessor

Accessor:存取器

可以看到HttpAccessor主要功能:

  • 提供請求工廠
  • 創建請求
public abstract class HttpAccessor {

    // 默認請求工廠
	private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

    // 同樣可以設置請求工廠
	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
		Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
		this.requestFactory = requestFactory;
	}

    // 獲取請求工廠
	public ClientHttpRequestFactory getRequestFactory() {
		return this.requestFactory;
	}

    // 創建請求
	protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
		ClientHttpRequest request = getRequestFactory().createRequest(url, method);
		if (logger.isDebugEnabled()) {
			logger.debug("Created " + method.name() + " request for \"" + url + "\"");
		}
		return request;
	}

}

(2)InterceptingHttpAccessor

這個類主要提供:設置這個RestTemplate的攔截器們

public abstract class InterceptingHttpAccessor extends HttpAccessor {

    // 攔截器列表
	private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();

    // 攔截器工廠
	@Nullable
	private volatile ClientHttpRequestFactory interceptingRequestFactory;

    // 設置攔截器
	public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
		// Take getInterceptors() List as-is when passed in here
		if (this.interceptors != interceptors) {
			this.interceptors.clear();
			this.interceptors.addAll(interceptors);
			AnnotationAwareOrderComparator.sort(this.interceptors);
		}
	}

    // 獲取攔截器列表
	public List<ClientHttpRequestInterceptor> getInterceptors() {
		return this.interceptors;
	}

    // 設置請求工廠 
	@Override
	public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
		super.setRequestFactory(requestFactory);
		this.interceptingRequestFactory = null;
	}

    // 獲取工廠
    // 若無攔截器,則返回父類的請求工廠; 若有攔截器,則返回自身的`InterceptingClientHttpRequestFactory`
	@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();
		}
	}

}

(3)RestOperations

這個接口主要提供對外服務。

public interface RestOperations {
    // ... ... 
}


二、RestTemplate請求流程


按這個請求來展開:
restTemplate.getForObject("/ping", String.class);

先講共同點,再講分歧。

  1. getForObject()
// RestTemplate.java
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
    // 請求回調
    RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
    
    // 消息提取器
    HttpMessageConverterExtractor<T> responseExtractor =
            new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
    // 執行 
    return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
  1. execute()
// RestTemplate.java
public <T> T execute(URI url, HttpMethod method, @Nullable RequestCallback requestCallback,
                     @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
    
    return doExecute(url, method, requestCallback, responseExtractor);
}

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;
    try {
        // 創建請求
        ClientHttpRequest request = createRequest(url, method);
        if (requestCallback != null) {
            requestCallback.doWithRequest(request);
        }
        // 執行請求
        response = request.execute();
        handleResponse(url, method, response);
        return (responseExtractor != null ? responseExtractor.extractData(response) : null);
    }
    catch (IOException ex) {
        // ...
    }
    finally {
        if (response != null) {
            response.close();
        }
    }
}

createRequest(url, method);, 這裏會出現不同的處理。

(1) createRequest(url, method);

不同的處理,主要是:有無攔截器

調用的工廠不同,處理也不同。

調用的是HttpAccessor方法

// HttpAccessor.java
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
    // 這一步開始有不同
    ClientHttpRequest request = getRequestFactory().createRequest(url, method);
    if (logger.isDebugEnabled()) {
        logger.debug("Created " + method.name() + " request for \"" + url + "\"");
    }
    return request;
}

當調用getRequestFactory()時,調用的是InterceptingHttpAccessor裏的

// InterceptingHttpAccessor.java
public ClientHttpRequestFactory getRequestFactory() {
    // 1. 
    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();
    }
}

可以看到若沒有攔截器,則直接調用父類HttpAccessor的方法:return super.getRequestFactory();
否則,new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);

1)new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);

// InterceptingClientHttpRequestFactory.java
public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
                                            @Nullable List<ClientHttpRequestInterceptor> interceptors) {

    super(requestFactory);
    this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());
}

// ClientHttpRequestFactory.java
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;

// InterceptingClientHttpRequestFactory.java
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
    return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}

以上是創建過程,ClientHttpRequest創建完成。

下面是執行過程 request.execute();

// RestTemplate.java
response = request.execute();

// ClientHttpRequest.java
public interface ClientHttpRequest extends HttpRequest, HttpOutputMessage {

	ClientHttpResponse execute() throws IOException;

}

// AbstractClientHttpRequest.java
public final ClientHttpResponse execute() throws IOException {
    assertNotExecuted();
    // 重點
    ClientHttpResponse result = executeInternal(this.headers);
    this.executed = true;
    return result;
}

executeInternal(this.headers);

// AbstractClientHttpRequest.java
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;

@Override
protected 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;
}

選擇跳轉到 InterceptingClientHttpRequest.java

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);
            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());
                }
            }
            return delegate.execute();
        }
    }
}

2)return super.getRequestFactory();

這個直接返回父類已經初始化好的 SimpleClientHttpRequestFactory

private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();

public ClientHttpRequestFactory getRequestFactory() {
    return this.requestFactory;
}

之後跟上面思路類似



三、問題


(1)什麼時候注入ClientHttpRequestInterceptor

創建RestTemplate時候,可以設置。

public class RestTemplateHeaderModifierInterceptor
  implements ClientHttpRequestInterceptor {
 
    @Override
    public ClientHttpResponse intercept(
      HttpRequest request, 
      byte[] body, 
      ClientHttpRequestExecution execution) throws IOException {
  
        ClientHttpResponse response = execution.execute(request, body);
        response.getHeaders().add("Foo", "bar");
        return response;
    }
}

@Configuration
public class RestClientConfig {
 
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
 
        List<ClientHttpRequestInterceptor> interceptors
          = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
            interceptors = new ArrayList<>();
        }
        // 這邊多添加一個自己自定義的
        interceptors.add(new RestTemplateHeaderModifierInterceptor());
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }
}

(2)RestTemplate 模板體現在哪?

模板體現在統一處理吧,如異常等。

但凡凡好像沒找到誒。



四、凡凡有話說


希望能提高閱讀代碼的能力吧。

下面是凡凡自己的看法:

看到一半這個RestTemplate設計的並不是很好。

  1. RestTemplateInterceptingHttpAccessor 並沒有直接關係

如果可以採用組合方式或許更好。
來談談RestTemplate的職責吧:1. 對外提供方法 2. 轉交處理請求
那麼 RestTemplate更像是一個工具人

  1. 有時雖然遵守 LSP 原則,但增加閱讀源碼的難度。

Debug可能好些,但凡凡好像太弱了,debug一直找不到自己想要的,所以就肉眼了。



五、參考資料


https://www.baeldung.com/spring-rest-template-interceptor

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章