上一節咱們學習瞭如何使用jdk的URI工具類發送http請求,這一節學習一下spring框架中對於jdk的網絡請求工具類的封裝RestTemplate.spring
@RestController
public class Controller {
@GetMapping("hello")
public String hello(){
return "hello";
}
}
複製代碼
首先仍是接着上一節的內容。先建立一個服務端,提供上述接口。json
template.getForObject("http://127.0.0.1:8763/hello",String.class);
複製代碼
接着使用RestTemplate來調用該服務。能夠看出,代碼量上,節省了很是多。那麼RestTemplate到底進行了哪些封裝呢?咱們從源碼來探究一下。bash
在這兒我就以基礎的getForObject方法來進行分析。網絡
String url, Class<T> responseType, Object... uriVariables
複製代碼
getForObject方法有三個參數app
參數名 | 做用 |
---|---|
url | 請求的地址 |
responseType | 返回值的類型 |
uriVariables | url裏面的填充字段 |
進入方法框架
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
複製代碼
在該方法中有三行代碼,第一行封裝了一個AcceptHeaderRequestCallback對象,第二行封裝了一個HttpMessageConverterExtractor對象,這兩對象的做用咱們能夠先放一放,而後第三行就是執行請求。工具
因此咱們進入execute方法post
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables)
throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
複製代碼
在excute方法中首先作了準備工做,將咱們的字符串url轉化成了URI對象。這個對象正是咱們上一節用以發送http請求的對象。在擴展的時候有一個expand方法,該方法的做用大體以下學習
Map<String,String> uriVariables = new HashMap<>();
uriVariables.put("name" ,"hello");
template.getForObject("http://127.0.0.1:8080/{name}",String.class,uriVariables);
複製代碼
在url中咱們使用{}輸入了一個佔位符name,而在uriVariables這個map中咱們設置了name的值爲hello,通過這個方法後,http://127.0.0.1:8080/{name} 就會變成 http://127.0.0.1:8080/hello 。ui
下面進入doExecute方法。
ClientHttpResponse response = null;
try {
//封裝一個request對象
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
//對request對象進行回調處理
requestCallback.doWithRequest(request);
}
//執行request請求
response = request.execute();
//處理返回結果
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
複製代碼
除開異常處理相關代碼,doExecute主要代碼如上。能夠看出主要邏輯仍是很清晰的。
首先建立ClientHttpRequest對象
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
複製代碼
openConnection和prepareConnection這兩個方法能夠本身看下,看看和上一節咱們本身調用URI對象有啥區別。
接下來對request對象進行回調處理
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (this.responseType != null) {
List<MediaType> allSupportedMediaTypes = getMessageConverters().stream()
.filter(converter -> canReadResponse(this.responseType, converter))
.flatMap(this::getSupportedMediaTypes)
.distinct()
.sorted(MediaType.SPECIFICITY_COMPARATOR)
.collect(Collectors.toList());
if (logger.isDebugEnabled()) {
logger.debug("Accept=" + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
複製代碼
get方法的回調處理只作了一件事,就是設置請求頭的"Accept"屬性。
執行request請求
public final ClientHttpResponse execute() throws IOException {
//校驗,確認請求未執行過
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
//執行完成,修改請求狀態
this.executed = true;
return result;
}
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;
}
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
//將咱們設置的請求頭加入connection中
addHeaders(this.connection, headers);
if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
this.connection.setDoOutput(false);
}
if (this.connection.getDoOutput() && this.outputStreaming) {
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
}
//創建鏈接,發送請求
this.connection.connect();
if (this.connection.getDoOutput()) {
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
}
複製代碼
處理返回結果
最後對結果的處理,其實猜也能猜到了,就是將返回的信息流封裝成咱們能夠直接操做的對象,通常最經常使用的應該就是json轉對象了吧。