Spring RestTemplate具體使用詳解
1、什么是REST?
REST(RepresentationalState Transfer)是Roy Fielding 提出的一個(gè)描述互聯(lián)系統(tǒng)架構(gòu)風(fēng)格的名詞。REST定義了一組體系架構(gòu)原則,您可以根據(jù)這些原則設(shè)計(jì)以系統(tǒng)資源為中心的Web 服務(wù),包括使用不同語(yǔ)言編寫(xiě)的客戶(hù)端如何通過(guò) HTTP處理和傳輸資源狀態(tài)。
為什么稱(chēng)為 REST?Web本質(zhì)上由各種各樣的資源組成,資源由URI 唯一標(biāo)識(shí)。瀏覽器(或者任何其它類(lèi)似于瀏覽器的應(yīng)用程序)將展示出該資源的一種表現(xiàn)方式,或者一種表現(xiàn)狀態(tài)。如果用戶(hù)在該頁(yè)面中定向到指向其它資源的鏈接,則將訪(fǎng)問(wèn)該資源,并表現(xiàn)出它的狀態(tài)。這意味著客戶(hù)端應(yīng)用程序隨著每個(gè)資源表現(xiàn)狀態(tài)的不同而發(fā)生狀態(tài)轉(zhuǎn)移,也即所謂REST。
2、REST成熟度的四個(gè)層次
第一個(gè)層次(Level0)的Web 服務(wù)只是使用 HTTP 作為傳輸方式,實(shí)際上只是遠(yuǎn)程方法調(diào)用(RPC)的一種具體形 式。SOAP和 XML-RPC都屬于此類(lèi)。
第二個(gè)層次(Level1)的Web 服務(wù)引入了資源的概念。每個(gè)資源有對(duì)應(yīng)的標(biāo)識(shí)符和表達(dá)。
第三個(gè)層次(Level2)的Web 服務(wù)使用不同的 HTTP 方法來(lái)進(jìn)行不同的操作,并且使用HTTP 狀態(tài)碼來(lái)表示不同的結(jié)果。如 HTTPGET 方法來(lái)獲取資源,HTTPDELETE 方法來(lái)刪除資源。
第四個(gè)層次(Level3)的Web 服務(wù)使用 HATEOAS。在資源的表達(dá)中包含了鏈接信息。客戶(hù)端可以根據(jù)鏈接來(lái)發(fā)現(xiàn)可以執(zhí)行的動(dòng)作。
其中第三個(gè)層次建立了創(chuàng)建、讀取、更新和刪除(create,read, update, and delete,CRUD)操作與 HTTP方法之間的一對(duì)一映射。根據(jù)此映射:
(1)若要在服務(wù)器上創(chuàng)建資源,應(yīng)該使用POST 方法。
(2)若要檢索某個(gè)資源,應(yīng)該使用GET 方法。
(3)若要更改資源狀態(tài)或?qū)ζ溥M(jìn)行更新,應(yīng)該使用PUT 方法。
(4)若要?jiǎng)h除某個(gè)資源,應(yīng)該使用DELETE 方法。
3、HTTP請(qǐng)求的方法
(1)GET:通過(guò)請(qǐng)求URI得到資源
(2)POST:用于添加新的內(nèi)容
(3)PUT:用于修改某個(gè)內(nèi)容,若不存在則添加
(4)DELETE:刪除某個(gè)內(nèi)容
(5)OPTIONS :詢(xún)問(wèn)可以執(zhí)行哪些方法
(6)HEAD :類(lèi)似于GET, 但是不返回body信息,用于檢查對(duì)象是否存在,以及得到對(duì)象的元數(shù)據(jù)
(7)CONNECT :用于代理進(jìn)行傳輸,如使用SSL
(8)TRACE:用于遠(yuǎn)程診斷服務(wù)器
4、HTTP請(qǐng)求的狀態(tài)碼
(1)成功Successful2xx:此類(lèi)狀態(tài)碼標(biāo)識(shí)客戶(hù)端的請(qǐng)求被成功接收、理解并接受。常見(jiàn)如200(OK)、204(NoContent)。
(2)重定向Redirection3xx:這個(gè)類(lèi)別的狀態(tài)碼標(biāo)識(shí)用戶(hù)代理要做出進(jìn)一步的動(dòng)作來(lái)完成請(qǐng)求。常見(jiàn)如301(MovedPermanently)、302(MovedTemprarily)。
(3)客戶(hù)端錯(cuò)誤Client Error 4xx:4xx類(lèi)別的狀態(tài)碼是當(dāng)客戶(hù)端象是出錯(cuò)的時(shí)使用的。常見(jiàn)如400(BadRequest)、401(Unauthorized)、403(Forbidden)、404(NotFound)。
(4)服務(wù)器錯(cuò)誤Server Error 5xx:響應(yīng)狀態(tài)碼以5開(kāi)頭表示服務(wù)器知道自己出錯(cuò)或者沒(méi)有能力執(zhí)行請(qǐng)求。常見(jiàn)如500(InternalServer Error)、502(BadGateway)、504(GatewayTimeout)。
5、RestTemplate
5.1 簡(jiǎn)介
Spring'scentral class for synchronous client-side HTTP access.It simplifies communication with HTTPservers, and enforces RESTful principles. Ithandles HTTP connections, leaving application code to provide URLs(with possible template variables) andextract results.
簡(jiǎn)單說(shuō)就是:簡(jiǎn)化了發(fā)起HTTP請(qǐng)求以及處理響應(yīng)的過(guò)程,并且支持REST。為什么說(shuō)簡(jiǎn)化了呢?
來(lái)看兩種實(shí)現(xiàn)方式
(1)使用java.net包下的URLConnection建立連接
String result= ""; BufferedReaderin = null; try { String urlNameString= url +"?" + param; URL realUrl= new URL(urlNameString); // 打開(kāi)和URL之間的連接 URLConnectionconnection = realUrlopenConnection(); // 設(shè)置通用的請(qǐng)求屬性 connectionsetRequestProperty("accept","*/*"); connectionsetRequestProperty("connection","Keep-Alive"); connectionsetRequestProperty("user-agent", "Mozilla/0(compatible; MSIE 0; Windows NT 1;SV1)"); // 建立實(shí)際的連接 connectionconnect(); // 獲取所有響應(yīng)頭字段 Map<String,List<String>> map = connectiongetHeaderFields(); // 遍歷所有的響應(yīng)頭字段 for(String key : mapkeySet()) { Systemoutprintln(key+ "--->" + mapget(key)); } // 定義 BufferedReader輸入流來(lái)讀取URL的響應(yīng) in =new BufferedReader(newInputStreamReader( connectiongetInputStream())); String line; while ((line = inreadLine())!= null) { result += line; } } catch (Exception e) { … } // 使用finally塊來(lái)關(guān)閉輸入流 finally{ // 關(guān)閉流 }
(2)使用RestTempalte
ResponseEntity<SsoUrlPrm>result = restTemplate.getForEntity(requestPathUrl,SsoUrlPrm.class);
5.2 對(duì)外開(kāi)放的接口
(1)
DELETE | delete |
GET | getForObject |
getForEntity | |
HEAD | headForHeaders |
OPTIONS | optionsForAllow |
POST | postForLocation |
postForObject | |
PUT | put |
any | exchange |
execute |
(2)每一個(gè)小類(lèi)又分三種,這三種有什么區(qū)別?* 第一種和第二種的首個(gè)參數(shù)都是用String表示一個(gè)URI。但它們的最后一個(gè)參數(shù)分別是Object[]和Map* 第三種的首個(gè)參數(shù)使用java.net.URI表示一個(gè)URI。且只有兩個(gè)參數(shù)
這是因?yàn)?,String類(lèi)型的URI支持占位符。比如:
restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}",String.class,"42", "21");
那么最終訪(fǎng)問(wèn)的URI為:http://example.com/hotels/42/bookings/21
但是String有一個(gè)小缺陷:String形式的URI會(huì)被URL編碼兩次(URL encode請(qǐng)自行百度),這就要求服務(wù)器在獲取URI中的參數(shù)時(shí)主動(dòng)進(jìn)行一次解碼,但如果服務(wù)的提供者不這么做呢?
這時(shí)就需要使用不會(huì)使用任何編碼的java.net.URI
PS:參數(shù)‘Class<T>responseType'定義了返回?cái)?shù)據(jù)的類(lèi)型。
(3)Exchange與其它接口的不同:
>允許調(diào)用者指定HTTP請(qǐng)求的方法(GET,POST,PUT等)
>可以在請(qǐng)求中增加body以及頭信息,其內(nèi)容通過(guò)參數(shù)‘HttpEntity<?>requestEntity'描述
>exchange支持‘含參數(shù)的類(lèi)型'(即泛型類(lèi))作為返回類(lèi)型,該特性通過(guò)‘ParameterizedTypeReference<T>responseType'描述。比如:
List<String> a = new ArrayList<String>(); System.out.println(a.getClass()); System.out.println(a.getClass().getGenericSuperclass()); ParameterizedTypeReference pt = new ParameterizedTypeReference<ArrayList<String>>() {}; System.out.println(pt.getType());
得到的結(jié)果是:
class java.util.ArrayList java.util.AbstractList<E> java.util.ArrayList<java.lang.String>
* 這是因?yàn)镻arameterizedTypeReference<ArrayList<String>>并不根據(jù)實(shí)參而是使用getGenericSuperclass()方法獲取其父類(lèi)的類(lèi)型(注意這里的new有花括號(hào),是ParameterizedTypeReference的子類(lèi)),父類(lèi)的類(lèi)型通過(guò)java.lang.reflect.Type描述,然后通過(guò)Type的getActualTypeArguments()獲得了父類(lèi)的實(shí)參類(lèi)型,注意得到的Type類(lèi),并不是class類(lèi)。
(4)excute
所有的get、post、delete、put、options、head、exchange方法最終調(diào)用的都是excute方法。舉個(gè)栗子:
@Override public <T> T getForObject(String url, Class<T> responseType, Object urlVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); return execute(url, HttpMethodGET, requestCallback, responseExtractor, urlVariables); }
Excute方法只是將String格式的URI轉(zhuǎn)成了java.net.URI,之后調(diào)用了doExecute方法。整個(gè)調(diào)用過(guò)程
6.doExcute
6.1 定義
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor) throws RestClientException {…}
這里需要了解兩個(gè)類(lèi): RequestCallback &ResponseExtractor
6.2RequestCallback
Callback interface for code that operates on a ClientHttpRequest. Allows to manipulate the request headers, and write to the request body.
簡(jiǎn)單說(shuō):用于操作請(qǐng)求頭和body,在請(qǐng)求發(fā)出前執(zhí)行。
該接口有兩個(gè)實(shí)現(xiàn)類(lèi):
AcceptHeaderRequestCallback | 只處理請(qǐng)求頭,用于getXXX()方法。 |
HttpEntityRequestCallback | 繼承于AcceptHeaderRequestCallback可以處理請(qǐng)求頭和body,用于putXXX()、postXXX()和exchange()方法。 |
* DELETE、HEAD、OPTIONS沒(méi)有使用這個(gè)接口。
6.3 發(fā)起請(qǐng)求
6.4ResponseExtractor
6.4.1 定義
Generic callback interface used by RestTemplate's retrieval methods Implementations of this interface perform the actual work of extracting data from a ClientHttpResponse, but don't need to worry about exception handling or closing resources.
簡(jiǎn)單說(shuō):解析HTTP響應(yīng)的數(shù)據(jù),而且不需要擔(dān)心異常和資源的關(guān)閉。
該接口有三個(gè)實(shí)現(xiàn)類(lèi):
HeadersExtractor | 用于提取請(qǐng)求頭。 |
HttpMessageConverterExtractor | 用于提取響應(yīng)body。 |
ResponseEntityResponseExtractor | 使用HttpMessageConverterExtractor提取body(委托模式),然后將body和響應(yīng)頭、狀態(tài)封裝成ResponseEntity對(duì)象。 |
6.4.2 提取響應(yīng)body
提取分三步:
(1)提取器HttpMessageConverterExtractor尋找可用的轉(zhuǎn)化器在默認(rèn)的RestTemplate的構(gòu)造函數(shù)中初始化了轉(zhuǎn)化器集合,包括:
轉(zhuǎn)化器 |
可轉(zhuǎn)化的類(lèi)型 |
ByteArrayHttpMessageConverter | byte[] |
StringHttpMessageConverter | String |
ResourceHttpMessageConverter | Resource |
SourceHttpMessageConverter | javax.xml.transform.* |
AllEncompassingFormHttpMessageConverter | MultiValueMap |
Jaxb2RootElementHttpMessageConverter | XmlRootElement,XmlType(注解) |
... | |
MappingJackson2HttpMessageConverter | Json |
除了前五個(gè),其他的轉(zhuǎn)化器會(huì)由classloader嘗試加載某個(gè)類(lèi)來(lái)判斷工程是否包含某個(gè)包,而后決定是否加入轉(zhuǎn)化器集合。
提取器遍歷轉(zhuǎn)化器集合以查找可用的轉(zhuǎn)化器,其中MappingJackson2HttpMessageConverter總是在最后一個(gè),因?yàn)樵擃?lèi)實(shí)現(xiàn)了GenericHttpMessageConverter,算是一個(gè)通用轉(zhuǎn)化器,只有在找不到合適的轉(zhuǎn)化器時(shí)才輪到它。Spring提供了一個(gè)該類(lèi)的實(shí)現(xiàn),以保證總是能得到該類(lèi)。
(2)轉(zhuǎn)化器尋找可用的反序列化器
轉(zhuǎn)化器持有一個(gè)反序列化器緩存集合,首先從緩存中尋找
如果已有可用的反序列化器,則直接返回。否則創(chuàng)建一個(gè)新的反序列化器。
反序列化器保存著待反序列化類(lèi)的域、方法、構(gòu)造器等信息,反序列化時(shí)就是使用構(gòu)造器創(chuàng)建了一個(gè)新的實(shí)例。
以jackson為例,創(chuàng)建反序列化器的過(guò)程在jackson-databind-xxx.jar中,有興趣的可以看一下。調(diào)用棧如下(由下往上找):
BeanDeserializerFactory.addBeanProps/addObjectIdReader/addReferenceProperties/addInjectables
BeanDeserializerFactory.buildBeanDeserializer
BeanDeserializerFactory.createBeanDeserializer
(3)反序列化器執(zhí)行反序列化
TOKEN |
Json的一個(gè)或一組字符 |
START_OBJECT | { |
END_OBJECT | } |
START_ARRAY | [ |
END_ARRAY | ] |
VALUE_TRUE | true |
VALUE_FALSE | false |
... |
調(diào)用棧:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解springboot解決第三方依賴(lài)jar包的問(wèn)題
本篇文章主要介紹了詳解springboot解決第三方依賴(lài)jar包的問(wèn)題,解決了第三方依賴(lài)jar包的問(wèn)題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Maven的配置文件pom.xml詳解(含常用plugin)
pom.xml是Maven項(xiàng)目的核心配置文件,它是 項(xiàng)目對(duì)象模型 - Project Object Model(POM)的縮寫(xiě),本文我們將全面解析pom.xml,了解其結(jié)構(gòu)和屬性,以及如何使用它來(lái)管理項(xiàng)目,感興趣的朋友跟隨小編一起看看吧2024-08-08springcloud gateway自定義斷言規(guī)則詳解,以后綴結(jié)尾進(jìn)行路由
這篇文章主要介紹了springcloud gateway自定義斷言規(guī)則詳解,以后綴結(jié)尾進(jìn)行路由,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java數(shù)據(jù)類(lèi)型超詳細(xì)示例講解
Java程序中要求參與的計(jì)算的數(shù)據(jù),必須要保證數(shù)據(jù)類(lèi)型的一致性,如果數(shù)據(jù)類(lèi)型不一致將發(fā)生類(lèi)型的轉(zhuǎn)換。本文將通過(guò)示例詳細(xì)說(shuō)說(shuō)Java中數(shù)據(jù)類(lèi)型的轉(zhuǎn)換,感興趣的可以了解一下2022-11-11SpringBoot測(cè)試時(shí)卡在Resolving Maven dependencies的問(wèn)題
這篇文章主要介紹了SpringBoot測(cè)試時(shí)卡在Resolving Maven dependencies的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之時(shí)間復(fù)雜度與空間復(fù)雜度
對(duì)于一個(gè)算法,其時(shí)間復(fù)雜度和空間復(fù)雜度往往是相互影響的,當(dāng)追求一個(gè)較好的時(shí)間復(fù)雜度時(shí),可能會(huì)使空間復(fù)雜度的性能變差,即可能導(dǎo)致占用較多的存儲(chǔ)空間,這篇文章主要給大家介紹了關(guān)于Java時(shí)間復(fù)雜度、空間復(fù)雜度的相關(guān)資料,需要的朋友可以參考下2022-02-02java多線(xiàn)程從入門(mén)到精通看這篇就夠了
熟悉 Java 多線(xiàn)程編程的同學(xué)都知道,當(dāng)我們線(xiàn)程創(chuàng)建過(guò)多時(shí),容易引發(fā)內(nèi)存溢出,因此我們就有必要使用線(xiàn)程池的技術(shù)了,今天通過(guò)本文給大家分享java多線(xiàn)程從入門(mén)到精通的相關(guān)知識(shí),一起看看吧2021-06-06spring boot+redis 監(jiān)聽(tīng)過(guò)期Key的操作方法
這篇文章主要介紹了spring boot+redis 監(jiān)聽(tīng)過(guò)期Key,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Springboot中實(shí)現(xiàn)接口冪等性的4種方案小結(jié)
本文主要介紹了Springboot中實(shí)現(xiàn)接口冪等性,包含數(shù)據(jù)庫(kù)的冪等,數(shù)據(jù)庫(kù)的冪等,Redis的冪等性和Token + 時(shí)間戳的冪等性,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03