詳解RestTemplate?用法
一、簡(jiǎn)言
RestTemplate 是從 Spring3.0 開(kāi)始支持的一個(gè) HTTP 請(qǐng)求工具,也有的稱之為網(wǎng)絡(luò)框架,說(shuō)白了就是Java版本的一個(gè)postman。專門用于在Java當(dāng)中服務(wù)與服務(wù)之間遠(yuǎn)程調(diào)用接口的時(shí)候用。
舉例A服務(wù),需要從B服務(wù)獲取到數(shù)據(jù),假如不經(jīng)過(guò)前端頁(yè)面只是后端A服務(wù)獲取B服務(wù)的數(shù)據(jù)的話,這時(shí)候就需要通過(guò)Java的HTTP請(qǐng)求工具來(lái)遠(yuǎn)程調(diào)用。
類似的還有Apache 的 HttpClient 以及OKHttp3,都是Java當(dāng)中常用的HTTP 請(qǐng)求工具。
關(guān)于HttpClient用法:http://www.dhdzp.com/article/256131.htm
RestTemplate優(yōu)點(diǎn):
- RestTemplate屬于spring的,假如springboot項(xiàng)目的話,完全不用引入任何其他依賴,直接可以用。
- RestTemplate可以和cloud當(dāng)中的ribbon進(jìn)行配合使用,只需要一個(gè)注解就可以完成負(fù)載均衡調(diào)用。
官網(wǎng)api:https://docs.spring.io/spring-framework/docs/5.0.9.RELEASE/javadoc-api/

二、注入容器
既然是遠(yuǎn)程調(diào)用,那么我們系統(tǒng)肯定不會(huì)只調(diào)用一次,通常情況我們會(huì)將RestTemplate注入到容器當(dāng)中,讓他保持單例,當(dāng)我們哪個(gè)類要使用的時(shí)候直接從容器里面獲取即可。這樣可以避免每調(diào)用一次創(chuàng)建一個(gè)RestTemplate對(duì)象。
2.1、普通配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextBean {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
使用的時(shí)候直接通過(guò)容器注入即可。
@Autowired private RestTemplate restTemplate;
2.2、詳細(xì)配置
對(duì)于一個(gè)請(qǐng)求來(lái)說(shuō),連接超時(shí)時(shí)間,請(qǐng)求超時(shí)時(shí)間等都是可以設(shè)置的參數(shù),為了更好的適應(yīng)業(yè)務(wù)需求,所以可以自己修改restTemplate的配置。如下利用@Bean注解的參數(shù)特性,形成了一個(gè)實(shí)例化鏈條。(我實(shí)際開(kāi)發(fā)當(dāng)中一般沒(méi)有詳細(xì)設(shè)置過(guò),包括我涉及到的項(xiàng)目也是,一般都是直接像上面的簡(jiǎn)單配置一下就開(kāi)始使用了)
@Bean注解修飾的方法,假如帶有參數(shù),其實(shí)就是代表@Bean所標(biāo)注的對(duì)象的實(shí)例化依賴于參數(shù)中的類,需要先將參數(shù)中的類實(shí)例化,才能實(shí)例化@Bean所標(biāo)注的對(duì)象。
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.HttpClientConnectionManager;
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.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class RestTemplateConfig {
/**
* http連接管理器
*
* @return
*/
@Bean
public HttpClientConnectionManager poolingHttpClientConnectionManager() {
/* 注冊(cè)http和https請(qǐng)求
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);*/
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// 最大連接數(shù)
poolingHttpClientConnectionManager.setMaxTotal(500);
// 同路由并發(fā)數(shù)(每個(gè)主機(jī)的并發(fā))
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);
return poolingHttpClientConnectionManager;
}
/**
* HttpClient
*
* @param poolingHttpClientConnectionManager
* @return
*/
@Bean
public HttpClient httpClient(HttpClientConnectionManager poolingHttpClientConnectionManager) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 設(shè)置http連接管理器
httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
// 設(shè)置重試次數(shù)
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true));
// 設(shè)置默認(rèn)請(qǐng)求頭(具體可根據(jù)自己業(yè)務(wù)場(chǎng)景進(jìn)行設(shè)置)
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("atoken", "WAKJDWAJLDKWAKDLKWALKDALKW"));
httpClientBuilder.setDefaultHeaders(headers);
return httpClientBuilder.build();
}
/**
* 請(qǐng)求連接池配置
*
* @param httpClient
* @return
*/
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
// httpClient創(chuàng)建器
clientHttpRequestFactory.setHttpClient(httpClient);
// 連接超時(shí)時(shí)間/毫秒(連接上服務(wù)器(握手成功)的時(shí)間,超出拋出connect timeout)
clientHttpRequestFactory.setConnectTimeout(5 * 1000);
// 數(shù)據(jù)讀取超時(shí)時(shí)間(socketTimeout)/毫秒(務(wù)器返回?cái)?shù)據(jù)(response)的時(shí)間,超過(guò)拋出read timeout)
clientHttpRequestFactory.setReadTimeout(10 * 1000);
// 連接池獲取請(qǐng)求連接的超時(shí)時(shí)間,不宜過(guò)長(zhǎng),必須設(shè)置/毫秒(超時(shí)間未拿到可用連接,會(huì)拋出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
clientHttpRequestFactory.setConnectionRequestTimeout(10 * 1000);
return clientHttpRequestFactory;
}
/**
* rest模板
*
* @return
*/
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) {
// boot中可使用RestTemplateBuilder.build創(chuàng)建
RestTemplate restTemplate = new RestTemplate();
// 配置請(qǐng)求工廠
restTemplate.setRequestFactory(clientHttpRequestFactory);
// 添加攔截器(這塊可以通過(guò)攔截器來(lái)實(shí)現(xiàn)日志打印,既然是攔截器,那肯定會(huì)丟失一定的性能,所以可根據(jù)情況使用,如果計(jì)劃在攔截器當(dāng)中打印request當(dāng)中的日志,一定要注意request是流,不能讀兩次,這塊要做的話,需要想辦法將流取出然后再替換一個(gè)新的流)
// List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
// interceptors.add(restTemplateLog);
// restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
三、GET請(qǐng)求

這里的方法一共有兩類,getForEntity 和 getForObject,每一類有三個(gè)重載方法,下面我們分別予以介紹,首先我們先了解一下傳參:
- String url:請(qǐng)求接口地址
- URI url:使用 Uri 對(duì)象時(shí),參數(shù)可以直接拼接在地址中
- Class<T> responseType:返回的response當(dāng)中body的對(duì)象類型,相當(dāng)于只要這塊寫好了,我們就不用將返回值 再進(jìn)行序列化成對(duì)象了。
- Map<String,?> uriVariables:map類型的key value參數(shù),這個(gè)key需要和地址當(dāng)中的key相對(duì)應(yīng)的
- Object... uriVariables:這是一個(gè)可變長(zhǎng)參數(shù),地址欄當(dāng)中可以用1,2數(shù)字占位符當(dāng)中參數(shù),傳參的時(shí)候,只要這塊的可變長(zhǎng)參數(shù)能按順序和占位符 一 一對(duì)上即可。
3.1、getForEntity
把這個(gè)接口當(dāng)做要遠(yuǎn)程調(diào)用的接口:
@GetMapping("/getTest2")
public String getTest2(String name) {
return "hello " + name + " !";
}
這種寫法實(shí)際是就相當(dāng)于是這樣的:http://localhost:8006/getTest2?name=222
那么遠(yuǎn)程調(diào)用怎么調(diào)用呢?下面一共提供了三種寫法!
@GetMapping("/test")
public void test() throws UnsupportedEncodingException {
// 第一種方案(這種方案有點(diǎn)類似hibernate占位符取值,getForEntity第三個(gè)參數(shù)就是一個(gè)可變長(zhǎng)參數(shù),當(dāng)get請(qǐng)求有多個(gè)參數(shù)的時(shí)候也是可以用這種方式的)
String url1 = "http://127.0.0.1:8006/getTest2?name={1}";
// 這塊的String.class就是ResponseEntity當(dāng)中的body要序列化成的java對(duì)象類型
ResponseEntity<String> responseEntity1 = restTemplate.getForEntity(url1, String.class, "aaa");
System.out.println(responseEntity1.getStatusCode());
System.out.println(responseEntity1.getBody());
System.out.println(responseEntity1.getHeaders());
// 第二種方案,將參數(shù)放入到一個(gè) map 中。map 中的 key 和占位符的 key 相對(duì)應(yīng),map 中的 value 就是參數(shù)的具體值
Map<String, Object> map = new HashMap<>();
String url2 = "http://127.0.0.1:8006/getTest2?name={name}";
map.put("name", "bbb");
ResponseEntity<String> responseEntity2 = restTemplate.getForEntity(url2, String.class, map);
System.out.println(responseEntity2.getStatusCode());
System.out.println(responseEntity2.getBody());
System.out.println(responseEntity2.getHeaders());
// 第三種方案,使用 Uri 對(duì)象時(shí),參數(shù)可以直接拼接在地址中
String url = "http://127.0.0.1:8006/getTest2?name=" + URLEncoder.encode("ccc", "UTF-8");
URI uri = URI.create(url);
ResponseEntity<String> responseEntity3 = restTemplate.getForEntity(uri, String.class);
System.out.println(responseEntity3.getStatusCode());
System.out.println(responseEntity3.getBody());
System.out.println(responseEntity3.getHeaders());
}
RestTemplate 發(fā)送的是 HTTP 請(qǐng)求,那么在響應(yīng)的數(shù)據(jù)中必然也有響應(yīng)頭,如果開(kāi)發(fā)者需要獲取響應(yīng)頭的話,那么就需要使用 getForEntity 來(lái)發(fā)送 HTTP 請(qǐng)求,此時(shí)返回的對(duì)象是一個(gè) ResponseEntity 的實(shí)例。通過(guò)ResponseEntity我們可以獲取請(qǐng)求的響應(yīng)詳細(xì)信息,例如:body請(qǐng)求體,header請(qǐng)求頭,status狀態(tài)碼

ResponseEntity對(duì)象,他實(shí)際上就是包含了例如網(wǎng)頁(yè)訪問(wèn)時(shí)候的響應(yīng)信息。

3.2、getForObject
getForObject 方法和 getForEntity 方法類似,getForObject 方法也有三個(gè)重載的方法,參數(shù)和 getForEntity 一樣,因此這里我就不重復(fù)介紹參數(shù)了,這里主要說(shuō)下 getForObject 和 getForEntity 的差異,這兩個(gè)的差異主要體現(xiàn)在返回值的差異上, getForObject 的返回值就是服務(wù)提供者返回的數(shù)據(jù),使用 getForObject 無(wú)法獲取到響應(yīng)頭。
把下面接口當(dāng)做要遠(yuǎn)程調(diào)用的接口:
// 沒(méi)有參數(shù)的時(shí)候調(diào)用
@GetMapping("/getTest1")
public String getTest1() {
return "getTest1";
}
// ?拼接的參數(shù)
@GetMapping("/getTest2")
public String getTest2(String name) {
return "hello " + name + " !";
}
// 斜杠地址欄/ 拼接的參數(shù)
@GetMapping("/getTest3/{name}")
public String getTest3(@PathVariable(value = "name") String name) {
return "hello " + name + " !";
}
那么遠(yuǎn)程調(diào)用怎么調(diào)用呢?下面提供多個(gè)示例供參考!
@GetMapping("/test1")
public void test1() throws UnsupportedEncodingException {
// 無(wú)參調(diào)用
String result = restTemplate.getForObject("http://127.0.0.1:8006/getTest1", String.class);
System.out.println(result);
String url2 = "http://127.0.0.1:8006/getTest2?name={1}";
String result1 = restTemplate.getForObject(url2, String.class, "aaa");
System.out.println(result1);
// 第一種方案 調(diào)用地址欄/拼接
String url3 = "http://127.0.0.1:8006/getTest3/{1}";
String result2 = restTemplate.getForObject(url3, String.class, "bbb");
System.out.println(result2);
// 第二種方案 調(diào)用地址欄/拼接
String url4 = "http://127.0.0.1:8006/getTest3/" + URLEncoder.encode("ccc", "UTF-8");
URI uri = URI.create(url4);
String result3 = restTemplate.getForObject(uri, String.class);
System.out.println(result3);
// 第三種方案 調(diào)用地址欄/拼接
Map<String, Object> map = new HashMap<>();
String url5 = "http://127.0.0.1:8006/getTest3/{name}";
map.put("name", "ddd");
String result4 = restTemplate.getForObject(url5, String.class, map);
System.out.println(result4);
}
通過(guò)以上練習(xí)后會(huì)發(fā)現(xiàn)一個(gè)致命問(wèn)題,提供的get請(qǐng)求的API當(dāng)中并沒(méi)有header傳參這一項(xiàng),假如請(qǐng)求的別的服務(wù)接口有認(rèn)證,那我們?cè)撛趺崔k呢?
我們可以使用RestTemplate 當(dāng)中的 exchange自定義請(qǐng)求。
四、POST 請(qǐng)求

可以看到,post 請(qǐng)求的方法類型除了 postForEntity 和 postForObject 之外,還有一個(gè) postForLocation。這里的方法類型雖然有三種,但是這三種方法重載的參數(shù)基本是一樣的。其余參數(shù)都和get一樣,就多了一個(gè)request參數(shù),如下:
Object request: 該參數(shù)可以是一個(gè)普通對(duì)象, 也可以是一個(gè)HttpEntity對(duì)象。
- 如果是一個(gè)普通對(duì)象, 而非HttpEntity對(duì)象的時(shí)候, RestTemplate會(huì)將請(qǐng)求對(duì)象轉(zhuǎn)換為一個(gè)HttpEntity對(duì)象來(lái)處理, 其中Object就是 request請(qǐng)求體(body)的類型, request內(nèi)容會(huì)被視作完整的body來(lái)處理;
- 如果 request 是一個(gè)HttpEntity對(duì)象, 那么就會(huì)被當(dāng)作一個(gè)完成的HTTP請(qǐng)求對(duì)象來(lái)處理, 這個(gè) request 中不僅包含了body的內(nèi)容, 也包含了header的內(nèi)容。
如下源碼是對(duì)request參數(shù)進(jìn)行轉(zhuǎn)換調(diào)用的:

什么情況下要使用HttpEntity?
假如我們遠(yuǎn)程調(diào)用的接口是有token認(rèn)證的,我們請(qǐng)求的時(shí)候就需要在header請(qǐng)求頭當(dāng)中添加token,這時(shí)候我們就需要通過(guò)HttpEntity對(duì)象來(lái)攜帶請(qǐng)求頭。
4.1、postForEntity
示例一:下面接口是JSON傳參,然后還獲取token了,把它當(dāng)做要遠(yuǎn)程調(diào)用的接口:
@PostMapping("/postTest2")
public User test2(HttpServletRequest request, @RequestBody User user1) {
System.out.println("接受的參數(shù):" + user1);
// 正常情況我們是需要通過(guò)攔截器來(lái)攔截request獲取請(qǐng)求頭當(dāng)中的token來(lái)進(jìn)行認(rèn)證,這塊只是演示
System.out.println("接受的請(qǐng)求頭:" + request.getHeader("token"));
User user = new User();
user.setId(1);
user.setName("張三");
user.setSex("男性");
return user;
}
那么遠(yuǎn)程調(diào)用怎么調(diào)用呢?下面示例供參考!
示例使用的是如下圖API,request 可以是一個(gè)普通對(duì)象,也可以是HttpEntity。

@GetMapping("/hello2")
public User hello5() {
String url = "http://127.0.0.1:8006/postTest2";
// 請(qǐng)求示例一:攜帶token請(qǐng)求頭
// 請(qǐng)求體
User user = new User();
user.setId(1);
user.setName("李四");
user.setSex("女性");
// 請(qǐng)求頭(這里有一點(diǎn)需要注意,HttpHeaders和HttpEntity都是引入的spring的包)
HttpHeaders headers = new HttpHeaders();
headers.add("token", "WDWADAWDWADAWSAXZCXZCXZEDF");
// 請(qǐng)求
HttpEntity<User> requst = new HttpEntity<>(user, headers);
ResponseEntity<User> responseEntity = restTemplate.postForEntity(url, requst, User.class);
System.out.println("響應(yīng)的結(jié)果:" + responseEntity.getBody());
// 請(qǐng)求示例二:不攜帶請(qǐng)求頭,直接傳普通對(duì)象,這里直接傳的user,map也是可以的,他都可以轉(zhuǎn)換成json,切記不能使用LinkedMultiValueMap,使用他不會(huì)轉(zhuǎn)換成json
ResponseEntity<User> responseEntity1 = restTemplate.postForEntity(url, user, User.class);
System.out.println("響應(yīng)的結(jié)果:" + responseEntity1.getBody());
return responseEntity1.getBody();
}
調(diào)用結(jié)果:

示例二:下面接口是key/value傳參,然后還獲取token了,把它當(dāng)做要遠(yuǎn)程調(diào)用的接口:
@PostMapping("/postTest1")
public String test1(HttpServletRequest request, String name) {
System.out.println("接受的參數(shù):" + name);
System.out.println("接受的請(qǐng)求頭:" + request.getHeader("token"));
return "Hello " + name + " !";
}
那么遠(yuǎn)程調(diào)用怎么調(diào)用呢?下面示例供參考!
下面每個(gè)示例都使用了request參數(shù),正常情況下,假如第三方接口沒(méi)有token認(rèn)證,我們是不需要使用request參數(shù)的,直接傳null即可。
@GetMapping("/hello1")
public void hello1() throws UnsupportedEncodingException {
// 請(qǐng)求頭(這里有一點(diǎn)需要注意,HttpHeaders和HttpEntity都是引入的spring的包)
HttpHeaders headers = new HttpHeaders();
headers.add("token", "WDWADAWDWADAWSAXZCXZCXZEDF");
HttpEntity<Map> requst = new HttpEntity<>(null, headers);
// 調(diào)用方式一,通過(guò)map傳參
String url1 = "http://127.0.0.1:8006/postTest1?name={name}";
Map<String, String> param = new HashMap<>(16);
param.put("name", "李四");
ResponseEntity<String> responseEntity1 = restTemplate.postForEntity(url1, requst, String.class, param);
System.out.println(responseEntity1.getBody());
// 調(diào)用方式二,通過(guò)占位符和可變長(zhǎng)參數(shù)傳參
String url2 = "http://127.0.0.1:8006/postTest1?name={1}";
ResponseEntity<String> responseEntity2 = restTemplate.postForEntity(url2, requst, String.class, "牛牛");
System.out.println(responseEntity2.getBody());
// 調(diào)用方式三,正常這塊其實(shí)都不需要URI對(duì)象了,根據(jù)地址直接訪問(wèn)就可以。
String url3 = "http://127.0.0.1:8006/postTest1?name=" + URLEncoder.encode("ccc", "UTF-8");
URI uri = URI.create(url3);
ResponseEntity<String> responseEntity3 = restTemplate.postForEntity(uri, requst, String.class);
System.out.println(responseEntity3.getBody());
// 調(diào)用方式四:這種方式是沒(méi)有使用請(qǐng)求頭的,使用LinkedMultiValueMap,切記不能使用hashmap和java實(shí)體類,不然就轉(zhuǎn)換成json了,將LinkedMultiValueMap放到HttpEntity的body當(dāng)中其實(shí)也是可以的,然后就可以攜帶token了。
String url4 = "http://127.0.0.1:8006/postTest1";
MultiValueMap map = new LinkedMultiValueMap();
map.add("name", "薩瓦迪卡");
ResponseEntity<String> responseEntity4 = restTemplate.postForEntity(url4, map, String.class);
System.out.println(responseEntity4.getBody());
}
調(diào)用示例:
前三種方式都是攜帶了header,最后一種沒(méi)有帶。

4.2、postForObject
postForObject 和 postForEntity 基本一致,就是返回類型不同而已,這里不再贅述。
4.3、postForLocation
postForLocation 方法的返回值是一個(gè) Uri 對(duì)象,因?yàn)?POST 請(qǐng)求一般用來(lái)添加數(shù)據(jù),有的時(shí)候需要將剛剛添加成功的數(shù)據(jù)的 URL 返回來(lái),此時(shí)就可以使用這個(gè)方法,一個(gè)常見(jiàn)的使用場(chǎng)景如用戶注冊(cè)功能,用戶注冊(cè)成功之后,可能就自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面了,此時(shí)就可以使用該方法。例如在 provider 中提供一個(gè)用戶注冊(cè)接口,再提供一個(gè)用戶登錄接口,如下:
@RequestMapping("/register")
public String register(User user) throws UnsupportedEncodingException {
return "redirect:/loginPage?username=" + URLEncoder.encode(user.getUsername(),"UTF-8") + "&address=" + URLEncoder.encode(user.getAddress(),"UTF-8");
}
@GetMapping("/loginPage")
@ResponseBody
public String loginPage(User user) {
return "loginPage:" + user.getUsername() + ":" + user.getAddress();
}
這里一個(gè)注冊(cè)接口,一個(gè)是登錄頁(yè)面,不過(guò)這里的登錄頁(yè)面我就簡(jiǎn)單用一個(gè)字符串代替了。然后在 consumer 中來(lái)調(diào)用注冊(cè)接口,如下:
@GetMapping("/hello8")
public String hello8() {
List<ServiceInstance> list = discoveryClient.getInstances("provider");
ServiceInstance instance = list.get(0);
String host = instance.getHost();
int port = instance.getPort();
String url = "http://" + host + ":" + port + "/register";
MultiValueMap map = new LinkedMultiValueMap();
map.add("username", "牧碼小子");
map.add("address", "深圳");
URI uri = restTemplate.postForLocation(url, map);
String s = restTemplate.getForObject(uri, String.class);
return s;
}
注意: postForLocation 方法返回的 Uri 實(shí)際上是指響應(yīng)頭的 Location 字段,所以,provider 中 register 接口的響應(yīng)頭必須要有 Location 字段(即請(qǐng)求的接口實(shí)際上是一個(gè)重定向的接口),否則 postForLocation 方法的返回值為null,初學(xué)者很容易犯這個(gè)錯(cuò)誤。
五、PUT請(qǐng)求
PUT 請(qǐng)求和POST請(qǐng)求傳參基本上一模一樣,他也有request參數(shù),唯一區(qū)別就是PUT請(qǐng)求是沒(méi)有返回值的,PUT 請(qǐng)求本身方法也比較少,只有三個(gè),如下:

六、DELETE請(qǐng)求
DELETE 請(qǐng)求和GET請(qǐng)求傳參基本上一模一樣,同樣都沒(méi)有request參數(shù),唯一區(qū)別就是DELETE 請(qǐng)求是沒(méi)有返回值的,DELETE 請(qǐng)求本身方法也比較少,只有三個(gè),如下:

七、LinkedMultiValueMap源碼分析
1、項(xiàng)目中在使用RestTemplate進(jìn)行http請(qǐng)求時(shí),會(huì)用到LinkedMultiValueMap,現(xiàn)在簡(jiǎn)單分析一下LinkedMultiValueMap的數(shù)據(jù)結(jié)構(gòu)。
2、打開(kāi)LinkedMultiValueMap的源碼,可以看到里面封裝的是一個(gè)Map,再看構(gòu)造方法,最終創(chuàng)建的是一個(gè)LinkedHashMap。這個(gè)Map的value有點(diǎn)特別,他不能放任何值,必須是一個(gè)List。
其實(shí)LinkedMultiValueMap可以看作是一個(gè)表單,在表單中,一個(gè)name可以對(duì)應(yīng)很多值,比如根據(jù)id批量刪除,一個(gè)key需要對(duì)應(yīng)很多需要?jiǎng)h除的id

3、add、put、set
public static void main(String[] args) {
MultiValueMap params = new LinkedMultiValueMap();
params.add("username", "張三");
params.add("username", "李四");
System.out.println(params);
params.put("password", Arrays.asList("1234"));
System.out.println(params);
params.set("username", "王五");
System.out.println(params);
}運(yùn)行結(jié)果:

4、通過(guò)源碼分析
add是把值放到list中,若map中沒(méi)有key對(duì)應(yīng)的list,先new一個(gè)LinkedList。

put直接接收l(shuí)ist

set不管原來(lái)怎么樣,都會(huì)重新new一個(gè)LinkedList,再把值放進(jìn)去

5、什么時(shí)候使用LinkedMultiValueMap,答案就是模擬普通的表單提交時(shí)。
HttpEntity的body里面使用LinkedMultiValueMap,那么就是key/value傳參,如果是其他的bean類型,就會(huì)格式化成json。
八、通用方法 exchange

為什么說(shuō)它通用呢?因?yàn)檫@個(gè)方法需要你在調(diào)用的時(shí)候去指定請(qǐng)求類型,即它既能做 GET 請(qǐng)求,也能做 POST 請(qǐng)求,也能做其它各種類型的請(qǐng)求。如果開(kāi)發(fā)者需要對(duì)請(qǐng)求進(jìn)行封裝,使用它再合適不過(guò)了。這個(gè)方法重載的方法非常多,其中參數(shù)也多了很多個(gè),如下:
HttpMethod method:請(qǐng)求的方法(GET、POST、PUT等)HttpEntity<?> requestEntity:HttpEntity對(duì)象,封裝了請(qǐng)求頭和請(qǐng)求體,在上面post的時(shí)候應(yīng)該掌握的差不多了。
8.1、用法示例
使用如下API演示兩個(gè)示例:
- post的key/value傳參
- post的json傳參

示例一:post的key/value傳參
@PostMapping("/exchangeTest1")
public String test1(HttpServletRequest request, String name) {
System.out.println("接受的參數(shù):" + name);
System.out.println("接受的請(qǐng)求頭:" + request.getHeader("token"));
return "Hello " + name + " !";
}
調(diào)用示例:
@GetMapping("/exchange1")
public void hello1() {
String url = "http://127.0.0.1:8006/exchangeTest1";
// 請(qǐng)求體,一定要用LinkedMultiValueMap,用hashmap或者javabean會(huì)直接轉(zhuǎn)換成json的
MultiValueMap map = new LinkedMultiValueMap();
map.add("name", "薩瓦迪卡");
// 請(qǐng)求頭(這里有一點(diǎn)需要注意,HttpHeaders和HttpEntity都是引入的spring的包)
HttpHeaders headers = new HttpHeaders();
headers.add("token", "WDWADAWDWADAWSAXZCXZCXZEDF");
// 請(qǐng)求
HttpEntity<Map> requst = new HttpEntity<>(map, headers);
ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, requst, String.class);
System.out.println("響應(yīng)的結(jié)果:" + exchange.getBody());
}調(diào)用結(jié)果:

示例二:post的json傳參
@PostMapping("/postTest2")
public User test2(HttpServletRequest request, @RequestBody User user1) {
System.out.println("接受的參數(shù):" + user1);
System.out.println("接受的請(qǐng)求頭:" + request.getHeader("token"));
User user = new User();
user.setId(1);
user.setName("張三");
user.setSex("男性");
return user;
}
調(diào)用示例:
@GetMapping("/exchange2")
public void hello2() {
String url = "http://127.0.0.1:8006/postTest2";
// 請(qǐng)求體,不要用LinkedMultiValueMap,否則會(huì)變成key/value傳參
User user = new User();
user.setId(1);
user.setName("李四");
user.setSex("女性");
// 請(qǐng)求頭(這里有一點(diǎn)需要注意,HttpHeaders和HttpEntity都是引入的spring的包)
HttpHeaders headers = new HttpHeaders();
headers.add("token", "WDWADAWDWADAWSAXZCXZCXZEDF");
// 請(qǐng)求
HttpEntity<User> requst = new HttpEntity<>(user, headers);
ResponseEntity<User> exchange = restTemplate.exchange(url, HttpMethod.POST, requst, User.class);
System.out.println("響應(yīng)的結(jié)果:" + exchange.getBody());
}
調(diào)用結(jié)果:

8.2、封裝通用util
可以寫一個(gè)通用的工具類,根據(jù)傳入的參數(shù)來(lái)確定請(qǐng)求的路徑和內(nèi)容。工具類肯定是想通過(guò)靜態(tài)方式來(lái)直接調(diào)用,而不是通過(guò)new Util()的方式來(lái)使用。但是靜態(tài)變量/類變量不是對(duì)象的屬性,而是一個(gè)類的屬性,spring則是基于對(duì)象層面上的依賴注入。所以我們不能@Autowired或者@resource一個(gè)靜態(tài)變量。但是靜態(tài)方法又不能調(diào)用非靜態(tài)的屬性,那么我們?cè)撛趺崔k呢?
@PostContruct是Java自帶的注解,在方法上加該注解會(huì)在項(xiàng)目啟動(dòng)的時(shí)候執(zhí)行該方法,也可以理解為在spring容器初始化的時(shí)候執(zhí)行該方法。
@Component
public class HttpUtil {
private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
@Resource
private RestTemplate restTemplate;
private static HttpUtil httpUtil;
@PostConstruct
public void init(){
httpUtil = this;
httpUtil.restTemplate = this.restTemplate;
}
public static <T> String httpRequest(String url, HttpMethod method, HttpEntity<T> entity,Class<T> responseType){
try {
//發(fā)起一個(gè)POST請(qǐng)求
ResponseEntity<String> result = httpUtil.restTemplate.exchange(url, method, entity, responseType);
return result.getBody();
} catch (Exception e) {
logger.error("請(qǐng)求失?。?" + e.getMessage());
}
return null;
}
}
這個(gè)時(shí)候我們就需要維護(hù)一個(gè)工具類的靜態(tài)實(shí)例,初始化的時(shí)候把restTemplate傳進(jìn)來(lái),這樣就可以直接調(diào)用HttpUtil.httpRequest()方法。
到此這篇關(guān)于詳解RestTemplate 用法的文章就介紹到這了,更多相關(guān)RestTemplate 用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java集合類ArrayList和Vector的區(qū)別面試精講
這篇文章主要為大家介紹了java集合類ArrayList和Vector的區(qū)別面試全面講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
使用IDEA創(chuàng)建一個(gè)vert.x項(xiàng)目的方法
這篇文章主要介紹了使用IDEA創(chuàng)建一個(gè)vert.x項(xiàng)目的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題
這篇文章主要介紹了使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
詳解servlet的url-pattern匹配規(guī)則
本篇文章主要介紹了=servlet的url-pattern匹配規(guī)則,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
關(guān)于JavaEE內(nèi)部類的部分注意事項(xiàng)
這篇文章主要介紹了關(guān)于JavaEE內(nèi)部類的部分注意事項(xiàng),將一個(gè)類定義在另一個(gè)類里面或者一個(gè)方法里面,這樣的類稱為內(nèi)部類,這是一種封裝思想,那么使用內(nèi)部類的時(shí)候要注意些什么呢,讓我們一起來(lái)看看吧2023-03-03
SpringBoot中@EnableAutoConfiguration和@Configuration的區(qū)別
這篇文章主要介紹了SpringBoot中@EnableAutoConfiguration和@Configuration的區(qū)別,@SpringBootApplication相當(dāng)于@EnableAutoConfiguration,@ComponentScan,@Configuration三者的集合,需要的朋友可以參考下2023-08-08
Java 如何使用Feign發(fā)送HTTP請(qǐng)求
這篇文章主要介紹了Java 如何使用Feign發(fā)送HTTP請(qǐng)求,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下2020-11-11

