一、RestTemplate是什么
環(huán)境約束:
spring-web-4.3.9.RELEASE
The
RestTemplate
is the core class for client-side access to RESTful services. It is conceptually similar to other template classes in Spring, such asJdbcTemplate
andJmsTemplate
and other template classes found in other Spring portfolio projects.RestTemplate’s behavior is customized by providing callback methods and configuring the HttpMessageConverter
used to marshal objects into the HTTP request body and to unmarshal any response back into an object. As it is common to use XML as a message format, Spring provides aMarshallingHttpMessageConverter
that uses the Object-to-XML framework that is part of theorg.springframework.oxm
package. This gives you a wide range of choices of XML to Object mapping technologies to choose from.This section describes how to use the
RestTemplate
and its associatedHttpMessageConverters
.
RestTemplate
是Spring的通過客戶端訪問RESTful服務(wù)端的核心類,和JdbcTemplate
、JmsTemplate
概念相似,都是Spring提供的模板類RestTemplate
的行為可以通過callback回調(diào)方法和配置HttpMessageConverter
來定制,用來把對象封裝到HTTP請求體,將響應(yīng)信息放到一個對象中
Invoking RESTful services in Java is typically done using a helper class such as Apache HttpComponents
HttpClient
. For common REST operations this approach is too low level as shown below.java中調(diào)用RESTful服務(wù)很典型的是使用
HttpClient
,對于常用的REST操作,這些方法屬于低等級的操作
String uri = "http://example.com/hotels/1/bookings";
PostMethod post = new PostMethod(uri);
String request = // create booking request content
post.setRequestEntity(new StringRequestEntity(request));
httpClient.executeMethod(post);
if (HttpStatus.SC_CREATED == post.getStatusCode()) {
Header location = post.getRequestHeader("Location");
if (location != null) {
System.out.println("Created new booking at :" + location.getValue());
}
}
使用HttpClient
我們需要自己封裝Post請求,再根據(jù)響應(yīng)的狀態(tài)碼判斷從響應(yīng)中獲取header和body,有時候還需要自己做json轉(zhuǎn)換
RestTemplate provides higher level methods that correspond to each of the six main HTTP methods that make invoking many RESTful services a one-liner and enforce REST best practices.
RestTemplate提供更高等級的符合HTTP的6中主要方法,可以很簡單的調(diào)用RESTful服務(wù)
二、創(chuàng)建RestTemplate
創(chuàng)建RestTemplate很簡單,只需要new RestTemplate()
,如果使用Spring架構(gòu),將創(chuàng)建的RestTemplate實(shí)例通過XML或注解的方式注冊到Spring容器中即可
舉例:
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
RestTemplate構(gòu)造方法
RestTemplate有3個構(gòu)造方法,一個無參構(gòu)造,兩個有參構(gòu)造
RestTemplate無參構(gòu)造
/**
* Create a new instance of the {@link RestTemplate} using default settings.
* Default {@link HttpMessageConverter}s are initialized.
* 使用默認(rèn)配置創(chuàng)建一個RestTemplate實(shí)例
* 默認(rèn)的HttpMessageConverter集合被初始化
*/
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
/**
* 如果類路徑下包含com.fasterxml.jackson.databind.ObjectMapper 和 com.fasterxml.jackson.core.JsonGenerator
* 使用jackson做http請求、響應(yīng)的json轉(zhuǎn)換
*/
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
}
參數(shù)為ClientHttpRequestFactory的構(gòu)造
/**
* Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
* @param requestFactory HTTP request factory to use
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
* 使用指定的ClientHttpRequestFactory創(chuàng)建一個RestTemplate實(shí)例
* requestFactory是用于創(chuàng)建HTTP請求的工廠,默認(rèn)的實(shí)現(xiàn)有
* SimpleClientHttpRequestFactory、HttpComponentsClientHttpRequestFactory
* 如果沒有設(shè)置默認(rèn)是SimpleClientHttpRequestFactory
*/
public RestTemplate(ClientHttpRequestFactory requestFactory) {
this(); //也會調(diào)用無參構(gòu)造初始化默認(rèn)的messageConverters
setRequestFactory(requestFactory);
}
參數(shù)為messageConverters的構(gòu)造
/**
* Create a new instance of the {@link RestTemplate} using the given list of
* {@link HttpMessageConverter} to use
* @param messageConverters the list of {@link HttpMessageConverter} to use
* @since 3.2.7
* 傳入自定義的HttpMessageConverter集合,并賦值給messageConverters,之后使用自定義的HttpMessageConverter
*/
public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
this.messageConverters.addAll(messageConverters);
}
三、RestTemplate API使用
RestTemplate 4.3.9.RELEASE版本 API
HTTP Method | RestTemplate Method |
---|---|
DELETE | void delete(String url, Object... uriVariables) <br /> void delete(String url, Map<String,?> uriVariables)<br />void delete(URI url) |
GET | <T> T getForObject(String url, Class<T> responseType, Object... uriVariables)<br /><T> T getForObject(String url, Class<T> responseType, Map<String,?> uriVariables)<br /><T> T getForObject(URI url, Class<T> responseType)<br /><br /><T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)<br /><T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String,?> uriVariables)<br /><T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) |
HEAD | HttpHeaders headForHeaders(String url, Object... uriVariables)<br />HttpHeaders headForHeaders(String url, Map<String,?> uriVariables)<br />HttpHeaders headForHeaders(URI url) |
OPTIONS | Set<HttpMethod> optionsForAllow(String url, Object... uriVariables)<br />Set<HttpMethod> optionsForAllow(String url, Map<String,?> uriVariables)<br />Set<HttpMethod> optionsForAllow(URI url) |
POST | <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)<br /><T> T postForObject(String url, Object request, Class<T> responseType, Map<String,?> uriVariables)<br /><T> T postForObject(URI url, Object request, Class<T> responseType)<br /><br /><T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)<br /><T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String,?> uriVariables)<br /><T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) |
PUT | void put(String url, Object request, Object... uriVariables)<br />void put(String url, Object request, Map<String,?> uriVariables)<br />void put(URI url, Object request) |
PATCH and others | exchange()、execute() |
-
RestTemplate的方法名遵循一定的命名規(guī)范,第一部分表示用哪種HTTP方法調(diào)用(get,post),第二部分表示返回類型
- getForObject() -- 發(fā)送GET請求,將HTTP response轉(zhuǎn)換成一個指定的object對象
- postForEntity() -- 發(fā)送POST請求,將給定的對象封裝到HTTP請求體,返回類型是一個HttpEntity對象
-
每個HTTP方法對應(yīng)的RestTemplate方法都有3種。其中2種的url參數(shù)為字符串,URI參數(shù)變量分別是Object數(shù)組和Map,第3種使用URI類型作為參數(shù)
-
注意,使用字符串類型的url默認(rèn)會對url進(jìn)行轉(zhuǎn)義,如
http://example.com/hotel list
在執(zhí)行時會轉(zhuǎn)義為http://example.com/hotel%20list
,這樣其實(shí)是沒有問題的,但如果字符串類型的url本身已經(jīng)轉(zhuǎn)義過了,執(zhí)行時就會再轉(zhuǎn)義一次,變成http://example.com/hotel%2520list
。如果不需要這種隱式的轉(zhuǎn)義,可以使用java.net.URI
參數(shù)的方法,這種方法不會在執(zhí)行時存在隱式的url轉(zhuǎn)義,可以在創(chuàng)建URI
對象時自行決定是否轉(zhuǎn)義,推薦使用UriComponentsBuilder
創(chuàng)建URI
UriComponents uriComponents = UriComponentsBuilder.fromUriString( "http://example.com/hotels/{hotel}/bookings/{booking}") .build() //build(true)就不會對url轉(zhuǎn)義,但如果包含http://example.com/hotel list這種需要轉(zhuǎn)義的url,會報錯 .expand("42", "21") .encode(); URI uri = uriComponents.toUri();
exchange
和execute
方法比上面列出的其它方法(如getForObject、postForEntity等)使用范圍更廣,允許調(diào)用者指定HTTP請求的方法(GET、POST、PUT等),并且可以支持像HTTP PATCH(部分更新),但需要底層的HTTP庫支持,JDK自帶的HttpURLConnection
不支持PATCH方法,Apache的HTTPClient 4.2及以后版本支持
-
GET方法
getForEntity()
發(fā)送GET請求,返回ResponseEntity
/**
* 參數(shù)1: String類型 或 URI類型的請求地址
* 參數(shù)2: 指定返回的實(shí)體類型,class對象
* 參數(shù)3: uri參數(shù),可以是變長數(shù)組或map
* 返回值:ResponseEntity<T>是Spring對HTTP響應(yīng)的封裝,包括了幾個重要的元素,如響應(yīng)碼、contentType、contentLength、response header信息,response body信息等
*/
@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
舉例:
ResponseEntity<Book> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8080/getbook?bookname={1}", Book.class, "java");
Book book = responseEntity.getBody(); //響應(yīng)體轉(zhuǎn)換為Book類型
int statusCodeValue = responseEntity.getStatusCodeValue(); //響應(yīng)狀態(tài)碼
HttpHeaders headers = responseEntity.getHeaders(); //響應(yīng)頭信息
getForObject()
發(fā)送GET請求,返回指定的Object類型
/**
* 參數(shù)1: String類型 或 URI類型的請求地址
* 參數(shù)2: 指定返回的實(shí)體類型,class對象
* 參數(shù)3: uri參數(shù),可以是變長數(shù)組或map
* 返回值:responseType指定的Object類型
*/
@Override
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
舉例:
Book book = restTemplate.getForObject("http://127.0.0.1:8080/getbook?bookname={1}", Book.class, "java");
POST方法
postForEntity()
發(fā)送POST請求,返回ResponseEntity
/**
* 參數(shù)1: String類型 或 URI類型的請求地址
* 參數(shù)2: 請求body,可以是HttpEntity類型(可設(shè)置request header),或其它Object類型
* 參數(shù)3: 指定返回的實(shí)體類型,class對象
* 參數(shù)4: uri參數(shù),可以是變長數(shù)組或map
* 返回值:ResponseEntity<T>是Spring對HTTP響應(yīng)的封裝,包括了幾個重要的元素,如響應(yīng)碼、contentType、contentLength、response header信息,response body信息等
*/
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
舉例:
//參數(shù)是Book類型,返回值是ResponseEntity<Book>類型
ResponseEntity<Book> responseEntity = restTemplate.postForEntity("http://127.0.0.1:8080/updateBook", book, Book.class);
Book book = responseEntity.getBody(); //響應(yīng)體轉(zhuǎn)換為Book類型
int statusCodeValue = responseEntity.getStatusCodeValue(); //響應(yīng)狀態(tài)碼
HttpHeaders headers = responseEntity.getHeaders(); //響應(yīng)頭信息
postForObject()
發(fā)送POST請求,返回指定的Object類型
/**
* 參數(shù)1: String類型 或 URI類型的請求地址
* 參數(shù)2: 請求body,可以是HttpEntity類型(可設(shè)置request header),或其它Object類型
* 參數(shù)3: 指定返回的實(shí)體類型,class對象
* 參數(shù)4: uri參數(shù),可以是變長數(shù)組或map
* 返回值:responseType指定的Object類型
*/
@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
舉例:
//參數(shù)是Book類型,返回值也是Book類型
Book book = restTemplate.postForObject("http://127.0.0.1:8080/updatebook", book, Book.class);
exchange方法
- 可以支持多種HTTP方法,在參數(shù)中指定
- 可以在請求中增加header和body信息,返回類型是ResponseEntity,可以從中獲取響應(yīng)的狀態(tài)碼,header和body等信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity requestEntity = new HttpEntity(requestHeaders);
HttpEntity<String> response = template.exchange(
"http://example.com/hotels/{hotel}",
HttpMethod.GET, //GET請求
requestEntity, //requestEntity,可以設(shè)置請求header、body
String.class, "42");
String responseHeader = response.getHeaders().getFirst("MyResponseHeader"); //響應(yīng)頭信息
String body = response.getBody();
四、RestTemplate擴(kuò)展/配置
1、處理請求頭和響應(yīng)頭
設(shè)置請求頭信息
(1)如果是發(fā)送post、put請求,要設(shè)置請求頭,可以在調(diào)用方法時的第二個參數(shù)傳入HttpEntity對象,HttpEntity可以用于設(shè)置請求頭信息,如
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity requestEntity = new HttpEntity(requestHeaders);
Book book = restTemplate.postForObject("http://127.0.0.1:8080/getbook", requestEntity, Book.class);
以postForObject()
方法舉例,其第二個參數(shù)接收Object類型的數(shù)據(jù),如傳入的是HttpEntity,則使用它作為整個請求實(shí)體,如果傳入的是其它Object類型,則將Object參數(shù)作為request body,新建一個HttpEntity作為請求實(shí)體
private HttpEntityRequestCallback(Object requestBody, Type responseType) {
super(responseType);
//如果是HttpEntity類型的,直接作為請求實(shí)體賦值給this.requestEntity
if (requestBody instanceof HttpEntity) {
this.requestEntity = (HttpEntity<?>) requestBody;
}
//如果requestBody不是HttpEntity類型,且不為空,以O(shè)bject參數(shù)作為request body,并新建HttpEntity
else if (requestBody != null) {
this.requestEntity = new HttpEntity<Object>(requestBody);
}
else {
this.requestEntity = HttpEntity.EMPTY;
}
}
(2)如果是其它HTTP方法調(diào)用要設(shè)置請求頭,可以使用exchange()方法,可以參考 官方示例
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity requestEntity = new HttpEntity(requestHeaders);
HttpEntity<String> response = template.exchange(
"http://example.com/hotels/{hotel}",
HttpMethod.GET, requestEntity, String.class, "42");
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
總之,設(shè)置request header信息,需要找到對應(yīng)的restTemplate方法中可以使用HttpEntity作為參數(shù)的,提前設(shè)置好請求頭信息
注意:HttpEntity有4個構(gòu)造方法,無參構(gòu)造,只設(shè)置請求body,只設(shè)置headers,既設(shè)置headers又設(shè)置body
/** * Create a new, empty {@code HttpEntity}. */ protected HttpEntity() { this(null, null); } /** * Create a new {@code HttpEntity} with the given body and no headers. * @param body the entity body */ public HttpEntity(T body) { this(body, null); } /** * Create a new {@code HttpEntity} with the given headers and no body. * @param headers the entity headers */ public HttpEntity(MultiValueMap<String, String> headers) { this(null, headers); } /** * Create a new {@code HttpEntity} with the given body and headers. * @param body the entity body * @param headers the entity headers */ public HttpEntity(T body, MultiValueMap<String, String> headers) { this.body = body; HttpHeaders tempHeaders = new HttpHeaders(); if (headers != null) { tempHeaders.putAll(headers); } this.headers = HttpHeaders.readOnlyHttpHeaders(tempHeaders); }
處理響應(yīng)頭信息
使用RestTemplate中xxxForEntity()
的方法,會返回ResponseEntity,可以從中獲取到響應(yīng)狀態(tài)碼,響應(yīng)頭和body等信息
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity requestEntity = new HttpEntity(requestHeaders);
HttpEntity<String> response = template.exchange(
"http://example.com/hotels/{hotel}",
HttpMethod.GET, requestEntity, String.class, "42");
//response相關(guān)信息
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
2、ClientHttpRequestFactory
ClientHttpRequestFactory是Spring定義的一個接口,其用于生產(chǎn)org.springframework.http.client.ClientHttpRequest
對象,RestTemplate只是模板類,抽象了很多調(diào)用方法,而底層真正使用何種框架發(fā)送HTTP請求是通過ClientHttpRequestFactory指定的
接口定義
/**
* Factory for {@link ClientHttpRequest} objects.
* Requests are created by the {@link #createRequest(URI, HttpMethod)} method.
* ClientHttpRequest對象的工廠
*
* @author Arjen Poutsma
* @since 3.0
*/
public interface ClientHttpRequestFactory {
/**
* Create a new {@link ClientHttpRequest} for the specified URI and HTTP method.
* <p>The returned request can be written to, and then executed by calling
* {@link ClientHttpRequest#execute()}.
* 使用指定的URI和HTTP方法新建一個ClientHttpRequest對象
* 可以修改返回的request,并通過ClientHttpRequest的execute()方法執(zhí)行調(diào)用
* 即調(diào)用的邏輯也被Spring封裝到ClientHttpRequest中
*
* @param uri the URI to create a request for
* @param httpMethod the HTTP method to execute
* @return the created request
* @throws IOException in case of I/O errors
*/
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
}
RestTemplate可以在構(gòu)造時設(shè)置ClientHttpRequestFactory,也可以通過setRequestFactory()方法設(shè)置
構(gòu)造方法設(shè)置:
/**
* Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}.
* @param requestFactory HTTP request factory to use
* @see org.springframework.http.client.SimpleClientHttpRequestFactory
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
*/
public RestTemplate(ClientHttpRequestFactory requestFactory) {
this();
setRequestFactory(requestFactory);
}
可以看到上面注釋中已經(jīng)給出了Spring的兩種ClientHttpRequestFactory的實(shí)現(xiàn)類SimpleClientHttpRequestFactory
和HttpComponentsClientHttpRequestFactory
SimpleClientHttpRequestFactory
如果什么都不設(shè)置,RestTemplate默認(rèn)使用的是SimpleClientHttpRequestFactory,其內(nèi)部使用的是jdk的java.net.HttpURLConnection
創(chuàng)建底層連接,默認(rèn)是沒有連接池的,connectTimeout和readTimeout都是 -1,即沒有超時時間
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
。。。。。。
private int connectTimeout = -1;
private int readTimeout = -1;
//創(chuàng)建Request
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
//bufferRequestBody默認(rèn)為true
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
/**
* Opens and returns a connection to the given URL.
* 打開并返回一個指定URL的連接
* <p>The default implementation uses the given {@linkplain #setProxy(java.net.Proxy) proxy} -
* if any - to open a connection.
* @param url the URL to open a connection to
* @param proxy the proxy to use, may be {@code null}
* @return the opened connection 返回類型為 java.net.HttpURLConnection
* @throws IOException in case of I/O errors
*/
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
URLConnection urlConnection = (proxy != null ? url.openConnection(proxy) : url.openConnection());
if (!HttpURLConnection.class.isInstance(urlConnection)) {
throw new IllegalStateException("HttpURLConnection required for [" + url + "] but got: " + urlConnection);
}
return (HttpURLConnection) urlConnection;
}
/**
* Template method for preparing the given {@link HttpURLConnection}.
* <p>The default implementation prepares the connection for input and output, and sets the HTTP method.
* @param connection the connection to prepare
* @param httpMethod the HTTP request method ({@code GET}, {@code POST}, etc.)
* @throws IOException in case of I/O errors
*/
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
//如果connectTimeout大于等于0,設(shè)置連接超時時間
if (this.connectTimeout >= 0) {
connection.setConnectTimeout(this.connectTimeout);
}
//如果readTimeout大于等于0,設(shè)置讀超時時間
if (this.readTimeout >= 0) {
connection.setReadTimeout(this.readTimeout);
}
connection.setDoInput(true);
if ("GET".equals(httpMethod)) {
connection.setInstanceFollowRedirects(true);
}
else {
connection.setInstanceFollowRedirects(false);
}
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
connection.setRequestMethod(httpMethod);
}
。。。。。。
}
HttpComponentsClientHttpRequestFactory
HttpComponentsClientHttpRequestFactory底層使用Apache HttpClient創(chuàng)建請求,訪問遠(yuǎn)程的Http服務(wù),可以使用一個已經(jīng)配置好的HttpClient實(shí)例創(chuàng)建HttpComponentsClientHttpRequestFactory請求工廠,HttpClient實(shí)例中可以配置連接池和證書等信息
添加HttpClient依賴
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>x.x.x</version> <!-- springboot項(xiàng)目不用指定 -->
</dependency>
設(shè)置超時時間
設(shè)置超時時間,可以直接使用Spring的底層基于HttpClient的HttpComponentsClientHttpRequestFactory,此處設(shè)置的是ClientHttpRequestFactory級別的全局超時時間
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
@Bean
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(30 * 1000); //連接超時時間
factory.setReadTimeout(60 * 1000); //讀超時時間
return factory;
}
}
注意:如果通過一個HttpClient實(shí)例創(chuàng)建HttpComponentsClientHttpRequestFactory,并通過HttpClient指定了DefaultRequestConfig,設(shè)置了connectTimeout、readTimeout等,在實(shí)際執(zhí)行請求創(chuàng)建request時會與HttpComponentsClientHttpRequestFactory的配置合并,connectTimeout、socketTimeout、connectionRequestTimeout 以HttpComponentsClientHttpRequestFactory的配置為準(zhǔn)
HttpComponentsClientHttpRequestFactory:
/**
* Merge the given {@link HttpClient}-level {@link RequestConfig} with
* the factory-level {@link RequestConfig}, if necessary.
* @param clientConfig the config held by the current httpClient級別的requestConfig配置
* @return the merged request config
* (may be {@code null} if the given client config is {@code null})
* @since 4.2
*/
protected RequestConfig mergeRequestConfig(RequestConfig clientConfig) {
if (this.requestConfig == null) { // nothing to merge
return clientConfig;
}
RequestConfig.Builder builder = RequestConfig.copy(clientConfig);
int connectTimeout = this.requestConfig.getConnectTimeout(); //HttpComponentsClientHttpRequestFactory級別的配置
if (connectTimeout >= 0) {
builder.setConnectTimeout(connectTimeout);
}
int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout();
if (connectionRequestTimeout >= 0) {
builder.setConnectionRequestTimeout(connectionRequestTimeout);
}
int socketTimeout = this.requestConfig.getSocketTimeout();
if (socketTimeout >= 0) {
builder.setSocketTimeout(socketTimeout);
}
return builder.build();
}
上例中雖然沒有指定http連接池,但** HttpComponentsClientHttpRequestFactory無參構(gòu)造會創(chuàng)建一個HttpClient,并默認(rèn)使用了連接池配置,MaxTotal=10,DefaultMaxPerRoute=5 **,具體如下:
HttpComponentsClientHttpRequestFactory:
/**
* Create a new instance of the {@code HttpComponentsClientHttpRequestFactory}
* with a default {@link HttpClient}.
*/
public HttpComponentsClientHttpRequestFactory() {
this(HttpClients.createSystem());
}
HttpClients:
/**
* Creates {@link CloseableHttpClient} instance with default
* configuration based on system properties.
* 創(chuàng)建CloseableHttpClient實(shí)例使用基于system properties的默認(rèn)配置
*/
public static CloseableHttpClient createSystem() {
return HttpClientBuilder.create().useSystemProperties().build();
}
HttpClientBuilder:
/**
* Use system properties when creating and configuring default
* implementations.
*/
public final HttpClientBuilder useSystemProperties() {
this.systemProperties = true; //設(shè)置systemProperties為true
return this;
}
public CloseableHttpClient build() {
HttpClientConnectionManager connManagerCopy = this.connManager; //沒有設(shè)置,為null
if (connManagerCopy == null) {
。。。。。。
//創(chuàng)建連接池管理器PoolingHttpClientConnectionManager
@SuppressWarnings("resource")
final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactoryCopy)
.build(),
null,
null,
dnsResolver,
connTimeToLive,
connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS);
if (defaultSocketConfig != null) {
poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
}
if (defaultConnectionConfig != null) {
poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
}
//由于是HttpClientBuilder.create().useSystemProperties().build(),systemProperties為true
if (systemProperties) {
String s = System.getProperty("http.keepAlive", "true"); //http.keepAlive默認(rèn)值為true
if ("true".equalsIgnoreCase(s)) {
s = System.getProperty("http.maxConnections", "5"); //默認(rèn)值為5
final int max = Integer.parseInt(s);
poolingmgr.setDefaultMaxPerRoute(max); //DefaultMaxPerRoute=5
poolingmgr.setMaxTotal(2 * max); //MaxTotal=10
}
}
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}
if (maxConnPerRoute > 0) {
poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
}
connManagerCopy = poolingmgr;
}
}
配置連接池
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(clientHttpRequestFactory());
}
@Bean
public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() {
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
//開始設(shè)置連接池
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager
= new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(100); //最大連接數(shù)
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20); //同路由并發(fā)數(shù)
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
HttpClient httpClient = httpClientBuilder.build();
// httpClient連接配置
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
= new HttpComponentsClientHttpRequestFactory(httpClient);
clientHttpRequestFactory.setConnectTimeout(30 * 1000); //連接超時
clientHttpRequestFactory.setReadTimeout(60 * 1000); //數(shù)據(jù)讀取超時時間
clientHttpRequestFactory.setConnectionRequestTimeout(30 * 1000); //連接不夠用的等待時間
return clientHttpRequestFactory;
}
catch (Exception e) {
logger.error("初始化clientHttpRequestFactory出錯", e);
}
return null;
}
}
3、自定義messageConverter
RestTemplate的無參構(gòu)造中默認(rèn)會初始化很多messageConverters,用于請求/響應(yīng)中的消息轉(zhuǎn)換
/**
* Create a new instance of the {@link RestTemplate} using default settings.
* Default {@link HttpMessageConverter}s are initialized.
* 使用默認(rèn)配置創(chuàng)建一個RestTemplate實(shí)例
* 默認(rèn)的HttpMessageConverter集合被初始化
*/
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
/**
* 如果類路徑下包含com.fasterxml.jackson.databind.ObjectMapper 和 com.fasterxml.jackson.core.JsonGenerator
* 使用jackson做http請求、響應(yīng)的json轉(zhuǎn)換
*/
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) { //類路徑下包含 com.google.gson.Gson
this.messageConverters.add(new GsonHttpMessageConverter());
}
}
springboot項(xiàng)目默認(rèn)使用jackson做json轉(zhuǎn)換
使用fastjson做json轉(zhuǎn)換
- 引入fastjson依賴
- 排除jackson的HttpMessageConverter轉(zhuǎn)換器
- 添加fastjson的轉(zhuǎn)換器
排除jackson的HttpMessageConverter轉(zhuǎn)換器有兩種方式:
(1)類路徑下去掉jackson的支持
從RestTemplate的無參構(gòu)造可以看出,需要判斷類路徑下是否有jackson的相關(guān)類,有才會添加MappingJackson2HttpMessageConverter,故可以在pom.xml中排除jackson的支持,以springboot項(xiàng)目舉例
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>jackson-databind</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
</exclusions>
</dependency>
(2)在初始化配置RestTemplate時,去掉其默認(rèn)的MappingJackson2HttpMessageConverter
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(clientHttpRequestFactory());
//restTemplate默認(rèn)的HttpMessageConverter
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
List<HttpMessageConverter<?>> messageConvertersNew = new ArrayList<HttpMessageConverter<?>>();
for(HttpMessageConverter httpMessageConverter : messageConverters){
//跳過MappingJackson2HttpMessageConverter
if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) continue;
messageConvertersNew.add(httpMessageConverter);
}
//添加fastjson轉(zhuǎn)換器
messageConvertersNew.add(fastJsonHttpMessageConverter());
return restTemplate;
}
@Bean
public HttpMessageConverter fastJsonHttpMessageConverter() {
//MediaType
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//FastJsonConfig
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue,
SerializerFeature.QuoteFieldNames);
//創(chuàng)建FastJsonHttpMessageConverter4 Spring 4.2后使用
FastJsonHttpMessageConverter4 fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter4();
fastJsonHttpMessageConverter.setSupportedMediaTypes(mediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
return fastJsonHttpMessageConverter;
}
五、SpringBoot中使用RestTemplate
SpringBoot項(xiàng)目可以通過上面的方式,@Bean往Spring容器中注冊一個配置好的RestTemplate實(shí)例,也可以參考 SpringBoot官方 的方式自定義RestTemplate
由于RestTemplate實(shí)例在使用前經(jīng)常需要自定義,SpringBoot沒有提供自動配置好的RestTemplate,但是自動配置好了可以用于創(chuàng)建RestTemplate的RestTemplateBuilder
實(shí)例,可以按如下使用
@Service
public class MyBean {
private final RestTemplate restTemplate;
public MyBean(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
RestTemplate自定義
RestTemplate自定義主要有三種方法,具體取決于希望自定義應(yīng)用的范圍
類范圍。為了盡量縮小自定義的范圍,在類中注入自動配置的
RestTemplateBuilder
,然后根據(jù)需求調(diào)用它的配置方法,每次調(diào)用配置方法都會 new RestTemplateBuilder()并返回,所以對RestTemplateBuilder的配置只會影響由它創(chuàng)建的RestTemplate-
應(yīng)用范圍。可以使用
RestTemplateCustomizer
來自定義應(yīng)用范圍的的RestTemplate,所有注冊到Spring容器的RestTemplateCustomizer
都會自動生效。如下,通過RestTemplateCustomizer設(shè)置連接池@Bean public RestTemplateCustomizer restTemplateCustomizer(){ return new RestTemplateCustomizer(){ @Override public void customize(RestTemplate restTemplate) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //創(chuàng)建連接管理器 PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(); poolingHttpClientConnectionManager.setMaxTotal(100); poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20); httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager); //創(chuàng)建httpClient HttpClient httpClient = httpClientBuilder.build(); //創(chuàng)建HttpComponentsClientHttpRequestFactory HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); httpComponentsClientHttpRequestFactory.setConnectTimeout(10 * 1000); httpComponentsClientHttpRequestFactory.setReadTimeout(60 * 1000); httpComponentsClientHttpRequestFactory.setConnectionRequestTimeout(20 * 1000); restTemplate.setRequestFactory(httpComponentsClientHttpRequestFactory); } }; }
- 最后,最極端的(也是很少使用的)選項(xiàng)是創(chuàng)建你自己的
RestTemplateBuilder
bean。這將關(guān)閉RestTemplateBuilder
的自動配置,并阻止使用任何RestTemplateCustomizer
bean