Spring restTemplate

什麼是RestTemplate

RestTemplate是Spring提供的用於訪問Rest服務的客戶端,提供了多種便捷訪問遠程HTTP服務的方法,可以大大提升客戶端的編寫效率。java

 

項目中注入RestTemplate

首先在項目中添加依賴:web

<!-- 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
spring

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(推薦使用)apache

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方法對應。json

HTTP方法 RestTemplate方法 說明
DELETE delete() 在特定的URL上對資源執行HTTP DELETE操做
GET

getForEntity() 安全

getForObject()restful

getForEntity():發送一個HTTP GET請求,返回的ResponseEntity包含了響應體所映射成的對象。 併發

getForObject():發送一個HTTP GET請求,返回根據響應體映射造成的對象app

POST

postForEntity() ide

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格式的字符串。

相關文章
相關標籤/搜索