SpringBoot 學習之RestTemplate

前言

概述

  RestTemplate 是 Spring 提供的用於訪問Rest服務的客戶端,RestTemplate 提供了多種便捷訪問遠程Http服務的方法,它簡化了與 http 服務的通訊方式,統一了 RESTful 的標準,封裝了 http 連接, 咱們只須要傳入 url 及返回值類型便可。相較於以前經常使用的 HttpClient,RestTemplate 是一種更優雅的調用 RESTful 服務的方式。默認狀況下,RestTemplate 默認依賴 jdk 的HTTP鏈接工具(HttpURLConnection),若是有須要的話也能夠經過setRequestFactory方法替換爲例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。java

該模板類的主要切入點爲如下幾個方法(其根據HTTP的六個方法制定六個主要方法)web

HTTPMethod RestTemplate Method 說明
Post postForLocation POST 數據到一個URL,返回新建立資源的URL
postForEntity POST 數據到一個URL,返回包含一個對象的ResponseEntity
postForObject POST 數據到一個URL,返回根據響應體匹配造成的對象
Get getForObject 發送一個HTTP GET請求,返回的請求體將映射爲一個對象
getForEntity 發送一個HTTP GET請求,返回的ResponseEntity包含了響應體所映射成的對象
Delete delete
head headForHeaders
put put
any exchange
execute 全部的get、post、delete、put、options、head、exchange方法最終調用的都是excute方法

對外開放的接口

對外開放的接口

  • [x] 在內部,RestTemplate默認使用HttpMessageConverter實例將HTTP消息轉換成POJO或者從POJO轉換成HTTP消息

RestTemplate是 spring 的一個 rest 客戶端,在 spring-web 這個包下,spring boot的依賴以下:spring

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

RestTemplate只是對其它Rest客戶端的一個封裝,自己並無本身的實現。在沒有第三方依賴的狀況下其默認實現是HttpURLConnection(集成了URLConnection),這是JDK自帶的REST客戶端實現。如今來看下其它最經常使用的幾種客戶端的引入apache

SimpleClientHttpRequestFactory(封裝URLConnection)
HttpComponentsClientHttpRequestFactory(封裝HttpClient)
OkHttp3ClientHttpRequestFactory(封裝OKHttp)

其切換與使用也很簡單,在pom中引入相應依賴併發

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
</dependency>

初始化RestTemplate

 在Config中的配置:app

package org.dllwh.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
	@Bean
	public RestTemplate restTemplate() {
		RestTemplate restTemplate = new RestTemplate();
		return restTemplate;
	}

	@Bean("urlConnection")
	public RestTemplate urlConnectionRestTemplate() {
		RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
		return restTemplate;
	}

	@Bean("httpClient")
	public RestTemplate httpClientRestTemplate() {
		RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
		return restTemplate;
	}

	@Bean("OKHttp3")
	public RestTemplate OKHttp3RestTemplate() {
		RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
		return restTemplate;
	}
}

 使用的時候通常都會只選擇其中的一種,因此上面的幾種配置任選其一。這裏僅僅只是演示說明socket

 RestTemplate前先得作一些初始化處理,好比指定http客戶端工廠類、設置超時時間、響應參數轉換器等。以 HttpComponents 爲例說明。函數

package org.dllwh.config;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
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.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
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.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class RestTemplateConfig {
	/** 創建鏈接的超時時間 */
	private static int	connectTimeout		= 20000;
	/** 鏈接不夠用的等待時間 */
	private static int	requestTimeout		= 20000;
	/** 每次請求等待返回的超時時間 */
	private static int	socketTimeout		= 30000;
	/** 每一個主機最大鏈接數 */
	private static int	defaultMaxPerRoute	= 100;
	/** 最大鏈接數 */
	private static int	maxTotalConnections	= 300;

	@Bean
	public RestTemplate buildRestTemplate(ClientHttpRequestFactory factory) {
		RestTemplate restTemplate = new RestTemplate(factory);
		restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
		return restTemplate;
	}

	/**
	 * @方法描述:建立HTTP客戶端工廠
	 * @return
	 */
	@Bean
	public HttpComponentsClientHttpRequestFactory createFactory() {
		// httpClient鏈接配置
		SSLContextBuilder builder = new SSLContextBuilder();

		try {
			TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
				public boolean isTrusted(X509Certificate[] chain, String authType) {
					return true;
				}
			};

			builder.loadTrustMaterial(null, acceptingTrustStrategy);
		} catch (Exception e) {
			log.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
		}

		SSLConnectionSocketFactory socketFactory = null;
		try {
			socketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
		} catch (KeyManagementException | NoSuchAlgorithmException e) {
			log.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
		}

		// 註冊http和https請求
		Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
				.register("http", PlainConnectionSocketFactory.getSocketFactory())
				.register("https", socketFactory).build();

		// 開始設置鏈接池
		PoolingHttpClientConnectionManager phccm = new PoolingHttpClientConnectionManager(registry);
		// 最大鏈接數
		phccm.setMaxTotal(maxTotalConnections);
		// 同路由併發數
		phccm.setDefaultMaxPerRoute(defaultMaxPerRoute);

		HttpClientBuilder httpClientBuilder = HttpClients.custom();
		httpClientBuilder.setSSLSocketFactory(socketFactory);
		httpClientBuilder.setConnectionManager(phccm);
		httpClientBuilder.setConnectionManagerShared(true);
		// 重試次數,默認是3次,沒有開啓
		httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));
		// 保持長鏈接配置,須要在頭添加Keep-Alive
		httpClientBuilder.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE);

		List<Header> headers = new ArrayList<>();
		headers.add(new BasicHeader("User-Agent", 
		    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.59 Safari/537.36"));
		headers.add(new BasicHeader("Connection", "keep-alive"));

		httpClientBuilder.setDefaultHeaders(headers);

		CloseableHttpClient httpClient = httpClientBuilder.build();

		// httpClient鏈接配置,底層是配置RequestConfig
		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();

		factory.setHttpClient(httpClient);
		// 鏈接超時
		factory.setConnectTimeout(connectTimeout);
		// 數據讀取超時時間,即SocketTimeout
		factory.setReadTimeout(socketTimeout);
		// 鏈接不夠用的等待時間,不宜過長,必須設置,好比鏈接不夠用時,時間過長將是災難性的
		factory.setConnectionRequestTimeout(requestTimeout);
		// 緩衝請求數據,默認值是true。經過POST或者PUT大量發送數據時,建議將此屬性更改成false,以避免耗盡內存。
		factory.setBufferRequestBody(false);
		return factory;
	}
}

GET請求

getForEntity

getForEntity() 的返回值類型 ResponseEntity,經過源碼能夠看到它繼承了 HttpEntity ,封裝了返回的響應信息,包括 響應狀態、響應頭、響應體等,獲取Http請求的所有信息。spring-boot

<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;
ResponseEntity response = restTemplate.getForEntity("http://localhost:8081/server",String.class);
response.getHeaders();		//響應頭
response.getStatusCode();	//響應碼
response.getBody();			//響應體,即前面的result
  • [ ] 第1個參數:要調用的服務的地址
  • [ ] 第2個參數:表示但願返回的body類型
  • [ ] 第3個以及之後參數:表示請求參數
  • 可使用map來封裝請求參數,並做爲getForObject的第三個參數,同時修改url以下,map中的"1"會替換url中的{1},"2"會替換url中的{2}
Map map = new HashMap();
map.put("1", "hello");
map.put("2", "world");
String result = restTemplate.getForObject("http://localhost:8081/server?param1={1}&param2={2}",
		String.class, map);
  • 也能夠直接將要傳遞的值放到getForObject方法的參數結尾,數量不限,它會按順序替換{1}和{2}
String result = restTemplate.getForObject("http://localhost:8081/server?param1={1}&param2={2}",
		String.class, "hello", "world");

getForObject

 getForObject 和 getForEntity 用法幾乎相同,其實是對getForEntity函數的進一步封裝,返回值返回的是 響應體,省去了咱們 再去 getBody()。工具

POST請求

postForObject

postForObject指post請求,並返回一個Object對象

String response = restTemplate.postForObject("http://localhost:8081/server?param1={1}&param2={2}",
		null, String.class, "hello", "world");
  • [ ] 第1個參數就是getForObject第1個參數。
  • [ ] 第2個參數爲null,其實是HttpEntity
  • [ ] 第3個參數就是getForObject第2個參數
  • [ ] 第4個及之後的參數就是getForObject第3個及之後的參數

postForObject除了第2個參數爲null,其它地方用法和getForObject是如出一轍的。可是post請求傳參一般不是寫在url上實現的,而是放在請求體中。此時,就須要使用第2個參數來傳參,同時可省略第4個參數的url傳參

Map map = new HashMap();
map.put("param1", "hello");
map.put("param2", "world");
String response = restTemplate.postForObject("http://localhost:8081/server", map, String.class);
  • [x] 注意,服務端端接收不一樣參數時,語法也有所不一樣
public String server(@RequestBody Map map,String param1,String param2)

postForEntity

<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;

postForLocation

PUT請求

 在RestTemplate中,PUT請求能夠經過put方法調用,put方法的參數和前面介紹的postForEntity方法的參數基本一致,只是put方法沒有返回值而已

@RequestMapping("put")
public void put() {
    restTemplate.put("http://http://localhost:8081/server/put/?userName={1}", '獨淚了無痕');
}

DELETE請求

 delete請求咱們能夠經過delete方法調用來實現

@RequestMapping("delete")
public void delete() {
    restTemplate.delete("http://localhost:8081/server/delete/{1}", 100);
}

Exchange請求

 exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不一樣之處在於它能夠指定請求的HTTP類型。

須要注意的一點是對於返回結果爲204 no content,這種沒有返回值的請求,RestTemplate會拋錯,有須要的話可使用httpClient的fluent

Excute請求

excute() 的用法與 exchange() 大同小異了,它一樣能夠指定不一樣的 HttpMethod,不一樣的是它返回的對象是響應體所映射成的對象 ,而不是 ResponseEntity 。

Excute方法的調用過程

相關文章
相關標籤/搜索