學(xué)習(xí)springboot ,RestTemplate的使用場(chǎng)景非常非常多,比如springcloud中的服務(wù)消費(fèi)谍椅。
我以前還自己去寫(xiě)http請(qǐng)求相關(guān)的交互,用的比較多的是apache httpcomponents ,后來(lái)在學(xué)springboot的過(guò)程中發(fā)現(xiàn)Spring的RestTemplate提供了一些更高級(jí)別的方法來(lái)滿足我們的功能。后來(lái)就把項(xiàng)目中原來(lái)的http交互都改成了RestTemplate。
下面來(lái)說(shuō)說(shuō)我在學(xué)習(xí)中的一些記錄和遇到的問(wèn)題(還是在使用的角度蜀踏,具體源碼剖析,大家可以自己翻看源碼):
1.RestTemplate 的引入:
先來(lái)看下RestTemplate 的類路徑:
org.springframework.web.client.RestTemplate
可以通過(guò)上面的路徑看出RestTemplate 是web下掰吕,項(xiàng)目中只需要加入spring-web的依賴就可以了果覆。我現(xiàn)在使用的spring版本是4.3.9.RELEASE。由于項(xiàng)目是基于springboot 的
在spring-boot-starter-web中已經(jīng)有了它的依賴殖熟。
2.RestTemplate 構(gòu)造:
RestTemplate有兩個(gè)構(gòu)造方法局待,分別是:
public RestTemplate() {
/**
...初始化過(guò)程
*/
}
public RestTemplate(ClientHttpRequestFactory requestFactory) {
this();
setRequestFactory(requestFactory);
}
其中,第一個(gè)進(jìn)行默認(rèn)初始化菱属,沒(méi)法進(jìn)行更多的限制和后續(xù)處理钳榨。如:設(shè)置超時(shí)時(shí)間...。第二個(gè)構(gòu)造方法中可以傳入ClientHttpRequestFactory參數(shù),ClientHttpRequestFactory接口的實(shí)現(xiàn)類中存在timeout屬性等等
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
我們可以在springboot的某個(gè)自定義的configure類中的restTemplate 構(gòu)造方法上添加
@Bean
RestTemplate restTemplate(){
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
將RestTemplate 實(shí)例注入spring容器中纽门。
調(diào)用時(shí)可以通過(guò):
@Autowired
private RestTemplate restTemplate ;
來(lái)使用薛耻。
3.RestTemplate 對(duì)HTTP Method的支持:
大家可以在圖中看到,Spring的RestTemplate提供了對(duì)這么多HTTP method的支持赏陵。一般來(lái)說(shuō)大家對(duì)GET,POST的使用場(chǎng)景比較多昭卓,因此下面以這兩個(gè)為例愤钾,簡(jiǎn)單的說(shuō)下它的使用瘟滨。
4.RestTemplate 使用實(shí)例(簡(jiǎn)單):
GET:
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException
使用方法:
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
POST:
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException
使用方法:
MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<String, String>();
bodyMap.setAll(urlVariables);
ResponseClass responseClass = restTemplate.postForObject(CAR_CES_URL, bodyMap, ResponseClass.class);
//更完整的:
HttpHeaders headers = new HttpHeaders();
headers.add("X-Auth-Token", "e348bc22-5efa-4299-9142-529f07a18ac9");
MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
postParameters.add("owner", "11");
postParameters.add("subdomain", "aoa");
postParameters.add("comment", "");
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(postParameters, headers);
ParseResultVo exchange = null;
try {
exchange = restTemplate.postForObject("http://demo", requestEntity, ParseResultVo.class);
logger.info(exchange.toString());
} catch (RestClientException e) {
logger.info("候醒。。杂瘸。倒淫。");
}
5.其他相關(guān)-異步調(diào)用(AsyncRestTemplate):
在很多場(chǎng)景中我們需要異步調(diào)用,我們使用RestTemplate的兄弟類AsyncRestTemplate败玉。 AsyncRestTemplate是在Spring4.0中對(duì)RestTemplate進(jìn)行擴(kuò)展產(chǎn)生的新類敌土,其為客戶端提供了異步http請(qǐng)求處理的一種機(jī)制,通過(guò)返回ListenableFuture對(duì)象生成回調(diào)機(jī)制运翼,以達(dá)到異步非阻塞發(fā)送http請(qǐng)求返干。
public String asyncReq(){
String url = "http://localhost:8080/jsonAsync";
ListenableFuture<ResponseEntity<JSONObject>> future = asyncRestTemplate.getForEntity(url, JSONObject.class);
future.addCallback(new SuccessCallback<ResponseEntity<JSONObject>>() {
public void onSuccess(ResponseEntity<JSONObject> result) {
System.out.println(result.getBody().toJSONString());
}
}, new FailureCallback() {
public void onFailure(Throwable ex) {
System.out.println("onFailure:"+ex);
}
});
return "this is async sample";
}
我這里使用的是futrue,可以帶返回參數(shù)的血淌。這是java多線程中的一部分內(nèi)容矩欠。如果有時(shí)間我會(huì)另起一篇簡(jiǎn)單的說(shuō)下Java的多線程。
6.請(qǐng)求ssl
參考我的另一篇文章:
RestTemplate設(shè)置headers,訪問(wèn)https實(shí)現(xiàn)ssl請(qǐng)求
7.遇到的問(wèn)題:
我在寫(xiě)微信的請(qǐng)求調(diào)用的時(shí)候出現(xiàn)了問(wèn)題悠夯,微信在文檔中說(shuō)返回的是json數(shù)據(jù)癌淮。但實(shí)際返回確是:text/plain。這時(shí)直接使用會(huì)出現(xiàn)類型轉(zhuǎn)換的錯(cuò)誤:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.solar.app.model.weixin.WxBaseUserInfo] and content type [text/plain]
但是由于默認(rèn)構(gòu)造的 MappingJackson2HttpMessageConverter(大家可以翻看源碼) 中的 supportedMediaTypes 只支持:application/json 的 MediaType沦补。
為此我們必須添加對(duì)它的支持:
public class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public WxMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_PLAIN);
setSupportedMediaTypes(mediaTypes);
}
}
我既不推薦把 WxMappingJackson2HttpMessageConverter 實(shí)例當(dāng)作構(gòu)造 RestTemplate 時(shí)的參數(shù)來(lái)構(gòu)造 RestTemplate乳蓄,也不推薦 使用新的 WxMappingJackson2HttpMessageConverter 替換 RestTemplate 默認(rèn)構(gòu)造中創(chuàng)建的 MappingJackson2HttpMessageConverter 實(shí)例,因?yàn)檫@兩種方式都會(huì)導(dǎo)致 Content-Type 為 application/json 的 Json 響應(yīng)沒(méi)有轉(zhuǎn)換器來(lái)反序列化夕膀,所以最佳的方式還是“追加”虚倒。
@Bean
RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
return restTemplate;
}
最后貼上一個(gè)完整實(shí)例:
@Configuration
public class RestTemplateConfig {
@Bean
@ConditionalOnMissingBean({ RestOperations.class, RestTemplate.class })
//Spring Boot的自動(dòng)配置機(jī)制依靠@ConditionalOnMissingBean注解判斷是否執(zhí)行初始化代碼,
// 即如果用戶已經(jīng)創(chuàng)建了bean产舞,則相關(guān)的初始化代碼不再執(zhí)行魂奥。
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
// return new RestTemplate(factory);
RestTemplate restTemplate = new RestTemplate(factory);
// 使用 utf-8 編碼集的 conver 替換默認(rèn)的 conver(默認(rèn)的 string conver 的編碼集為"ISO-8859-1")
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> converter = iterator.next();
if (converter instanceof StringHttpMessageConverter) {
iterator.remove();
}
}
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
//解決微信返回text/plain的解析
restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter());
return restTemplate;
}
@Bean
@ConditionalOnMissingBean({ClientHttpRequestFactory.class})
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(15000);// ms
factory.setConnectTimeout(15000);// ms
return factory;
}
}
7.參考資料:
RestTemplate 微信接口 text/plain HttpMessageConverter
基于AsyncRestTemplate異步HTTP請(qǐng)求的一種輕量級(jí)技術(shù)實(shí)現(xiàn)