Spring restTemplate
什麽是RestTemplate
RestTemplate是Spring提供的用於訪問Rest服務的客戶端,提供了多種便捷訪問遠程HTTP服務的方法,能夠大大提高客戶端的編寫效率。
項目中註入RestTemplate
首先在項目中添加依賴:
<!-- Jackson對自動解析JSON和XML格式的支持 --> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <!-- HttpClient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
在註入RestTemplate的bean的時候,可以通過ClientHtppRequestFactory指定RestTemplate發起HTTP請求的底層實現所采用的類庫。對此,ClientHttpRequestFactory接口主要提供了以下兩種方法:
一種是SimpleClientHttpRequestFactory,使用J2SE提供的方式(即java.net包提供的方式)創建底層的HTTP請求連接。
另一種是使用HttpComponentsClientHttpRequestFactory方式,底層使用HttpClient訪問遠程的http服務,使用HttpClient可以配置連接池和證書等信息。
以下的兩個方法都采用線程安全的單例(懶漢模式)
(1)SimpleClientHttpRequestFactory
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @Component @Lazy(false) public class SimpleRestClient { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleRestClient.class); private static RestTemplate restTemplate; static { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); requestFactory.setReadTimeout(5000); requestFactory.setConnectTimeout(5000); // 添加轉換器 List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8"))); messageConverters.add(new FormHttpMessageConverter()); messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); messageConverters.add(new MappingJackson2HttpMessageConverter()); restTemplate = new RestTemplate(messageConverters); restTemplate.setRequestFactory(requestFactory); restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); LOGGER.info("SimpleRestClient初始化完成"); } private SimpleRestClient() { } @PostConstruct public static RestTemplate getClient() { return restTemplate; } }
(2)HttpComponentsClientHttpRequestFactory(推薦使用)
import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.http.Header; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { /** * 返回RestTemplate * @param factory * @return */ @Bean public RestTemplate restTemplate(ClientHttpRequestFactory factory){ //消息轉換器,Spring Boot環境可省略,只需要添加相關依賴即可 // List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); // messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8"))); // messageConverters.add(new FormHttpMessageConverter()); // messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); // messageConverters.add(new MappingJackson2HttpMessageConverter()); RestTemplate restTemplate = new RestTemplate(factory); // restTemplate.setMessageConverters(messageConverters); return restTemplate; } /** * ClientHttpRequestFactory接口的另一種實現方式(推薦使用),即: * HttpComponentsClientHttpRequestFactory:底層使用Httpclient連接池的方式創建Http連接請求 * @return */ @Bean public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory(){ //Httpclient連接池,長連接保持30秒 PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS); //設置總連接數 connectionManager.setMaxTotal(1000); //設置同路由的並發數 connectionManager.setDefaultMaxPerRoute(1000); //設置header List<Header> headers = new ArrayList<Header>(); headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.04")); headers.add(new BasicHeader("Accept-Encoding", "gzip, deflate")); headers.add(new BasicHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")); headers.add(new BasicHeader("Connection", "keep-alive")); //創建HttpClient HttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(connectionManager) .setDefaultHeaders(headers) .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)) //設置重試次數 .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()) //設置保持長連接 .build(); //創建HttpComponentsClientHttpRequestFactory實例 HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); //設置客戶端和服務端建立連接的超時時間 requestFactory.setConnectTimeout(5000); //設置客戶端從服務端讀取數據的超時時間 requestFactory.setReadTimeout(5000); //設置從連接池獲取連接的超時時間,不宜過長 requestFactory.setConnectionRequestTimeout(200); //緩沖請求數據,默認為true。通過POST或者PUT大量發送數據時,建議將此更改為false,以免耗盡內存 requestFactory.setBufferRequestBody(false); return requestFactory; } }
RestTemplate提供了很多方法,可以與HTTP方法對應。
HTTP方法 | RestTemplate方法 | 說明 |
---|---|---|
DELETE | delete() | 在特定的URL上對資源執行HTTP DELETE操作 |
GET |
getForEntity() getForObject() |
getForEntity():發送一個HTTP GET請求,返回的ResponseEntity包含了響應體所映射成的對象。 getForObject():發送一個HTTP GET請求,返回根據響應體映射形成的對象 |
POST |
postForEntity() postForLocation() postForObject() |
postForEntity(): POST數據到一個URL,返回的ResponseEntity包含了響應體所映射成的對象。 postForLocation(): POST數據到一個URL,返回新創建資源的URL。 postForObject(): POST數據到一個URL,返回根據響應體映射形成的對象 |
PUT | put() | PUT資源到特定的URL |
HEAD | headForHeaders() | 發送HTTP HEAD請求,返回包含特定資源URL的HTTP頭 |
OPTIONS | optionsForAllow() | 發送HTTP OPTIONS請求,返回對特定URL的Allow頭信息 |
PATCH and others |
exchange() execute() |
exchange(): 在URL上執行特定的HTTP方法,返回的ResponseEntity包含了響應體所映射成的對象 execute():在URL上執行特定的HTTP方法,返回一個從響應體映射得到的對象 |
接下來講一下RestTemplate的這幾個方法如何使用。
GET請求方式的兩種方法
1. getForEntity()
三個getForEntity()方法的簽名:
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException; <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException; <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException;
示例代碼:
/** * 測試Get請求返回詳細信息,包括:響應正文、響應狀態碼、響應Header等 */ @Test public void testGetMethod3(){ //第一個參數為要調用的服務的地址,第二個參數為返回值的類型,第三和第四個參數時url中的傳參 ResponseEntity<DemoObj> responseEntity = restTemplate.getForEntity("http://127.0.0.1:9090/rest/testJson2?id={1}&name={2}" , DemoObj.class , 1,"Tom"); DemoObj body = responseEntity.getBody(); int statusCodeValue = responseEntity.getStatusCodeValue(); HttpHeaders headers = responseEntity.getHeaders(); System.out.println("responseEntity.getBody():" + body); System.out.println("responseEntity.getStatusCodeValue():" + statusCodeValue); System.out.println("responseEntity.getHeaders():" + headers); }
結果:
responseEntity.getBody():DemoObj [id=2, name=Tom Ret] responseEntity.getStatusCodeValue():200 responseEntity.getHeaders():{Date=[Fri, 09 Feb 2018 06:22:28 GMT], Content-Type=[application/json;charset=utf-8], Transfer-Encoding=[chunked]}
再來看一個Map傳參的例子:
@RequestMapping("/sayhello2") public String sayHello2() { Map<String, String> map = new HashMap<>(); map.put("name", "李四"); ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/sayhello?name={name}", String.class, map); return responseEntity.getBody(); }
當然,第一個參數也可以是URI而不是字符串,可以通過Spring中的UriComponents來構建uri即可。
@RequestMapping("/sayhello3") public String sayHello3() { UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/sayhello?name={name}").build().expand("王五").encode(); URI uri = uriComponents.toUri(); ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class); return responseEntity.getBody(); }
2.getForObject()
getForObject()實際上是對getForEntity()的進一步封裝,用法類似,唯一的區別就是getForObject()方法只返回請求類型的對象,而getForEntity()會返回請求的對象以及響應的Header,響應狀態碼等額外信息。
@RequestMapping("/book2") public Book book2() { Book book = restTemplate.getForObject("http://HELLO-SERVICE/getbook1", Book.class); return book; }
@Test public void testGetMethod2(){ Map<String, String> uriVariables = new HashMap<String, String>(); uriVariables.put("var_id", "1"); uriVariables.put("var_name", "Tom"); DemoObj obj = restTemplate.getForObject("http://127.0.0.1:9090/rest/testJson2?id={var_id}&name={var_name}" , DemoObj.class , uriVariables); System.out.println(obj); }
POST請求的三種方法
1. postForEntity()
POST請求和GET請求類似,也是三種方法:
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) throws RestClientException; <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException; <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException;
看個示例:
@Test public void testPostMethod2(){ DemoObj request = new DemoObj(1l, "Tim"); ResponseEntity<DemoObj> responseEntity = restTemplate.postForEntity("http://127.0.0.1:9090/rest/testJson1" , request, DemoObj.class); DemoObj body = responseEntity.getBody(); int statusCodeValue = responseEntity.getStatusCodeValue(); HttpHeaders headers = responseEntity.getHeaders(); System.out.println("responseEntity.getBody():" + body); System.out.println("responseEntity.getStatusCodeValue():" + statusCodeValue); System.out.println("responseEntity.getHeaders():" + headers); }
結果:
responseEntity.getBody():DemoObj [id=2, name=Tim Ret] responseEntity.getStatusCodeValue():200 responseEntity.getHeaders():{Date=[Fri, 09 Feb 2018 06:32:02 GMT], Content-Type=[application/json;charset=utf-8], Transfer-Encoding=[chunked]}
2.postForObject()
@Test public void testPostMethod1(){ DemoObj request = new DemoObj(1l, "Tim"); DemoObj obj = restTemplate.postForObject("http://127.0.0.1:9090/rest/testJson1" , request, DemoObj.class); System.out.println(obj); }
3.postForLocation()
postForLocation()也是提交新資源,提交成功之後,返回新資源的URI,postForLocation的參數和前面兩種的參數基本一致,只不過該方法的返回值為Uri,這個只需要服務提供者返回一個Uri即可,該Uri表示新資源的位置。
PUT請求
在RestTemplate中,PUT請求可以通過put方法調用,put方法的參數和前面介紹的postForEntity方法的參數基本一致,只是put方法沒有返回值而已。
@RequestMapping("/put") public void put() { Book book = new Book(); book.setName("紅樓夢"); restTemplate.put("http://HELLO-SERVICE/getbook3/{1}", book, 99); }
DELETE請求
@RequestMapping("/delete") public void delete() { restTemplate.delete("http://HELLO-SERVICE/getbook4/{1}", 100); }
exchange()方法執行指定的HTTP請求
exchange()方法同上面的很多方法不同的是,它可以指定請求的HTTP方式。
@Test public void testExchange(){ //設置header HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/x-zifangsky"); //設置參數 String requestBody = "1#Converter"; HttpEntity<String> requestEntity = new HttpEntity<String>(requestBody,headers); ResponseEntity<String> responseEntity = restTemplate.exchange("http://127.0.0.1:9090/convert" , HttpMethod.POST, requestEntity, String.class); System.out.println("responseEntity.getBody():" + responseEntity.getBody()); System.out.println("responseEntity.getHeaders():" + responseEntity.getHeaders()); }
手動指定轉換器
調用restful接口傳遞的數據是json格式的字符串,返回的響應也是json格式的字符串。
Spring restTemplate