Spring RestTemplate詳解

一、什麼是REST?

  REST(RepresentationalState Transfer)是Roy Fielding 提出的一個描述互聯繫統架構風格的名詞。REST定義了一組體系架構原則,您能夠根據這些原則設計以系統資源爲中心的Web 服務,包括使用不一樣語言編寫的客戶端如何經過 HTTP處理和傳輸資源狀態。html

  爲何稱爲 REST?Web本質上由各類各樣的資源組成,資源由URI 惟一標識。瀏覽器(或者任何其它相似於瀏覽器的應用程序)將展現出該資源的一種表現方式,或者一種表現狀態。若是用戶在該頁面中定向到指向其它資源的連接,則將訪問該資源,並表現出它的狀態。這意味着客戶端應用程序隨着每一個資源表現狀態的不一樣而發生狀態轉移,也即所謂REST。java

附:REST定義RESTSOAP的比較web

二、REST成熟度的四個層次

  第一個層次(Level0)的Web 服務只是使用 HTTP 做爲傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形  式。SOAP和 XML-RPC都屬於此類瀏覽器

  第二個層次(Level1)的Web 服務引入了資源的概念。每一個資源有對應的標識符和表達。緩存

  第三個層次(Level2)的Web 服務使用不一樣的 HTTP 方法來進行不一樣的操做,而且使用HTTP 狀態碼來表示不一樣的結果。如 HTTPGET 方法來獲取資源,HTTPDELETE 方法來刪除資源。服務器

  第四個層次(Level3)的Web 服務使用 HATEOAS。在資源的表達中包含了連接信息。客戶端能夠根據連接來發現能夠執行的動做。restful

 

  其中第三個層次創建了建立、讀取、更新和刪除(create,read, update, and delete,CRUD)操做與 HTTP方法之間的一對一映射。根據此映射:架構

(1)若要在服務器上建立資源,應該使用POST 方法。app

(2)若要檢索某個資源,應該使用GET 方法。ide

(3)若要更改資源狀態或對其進行更新,應該使用PUT 方法。

(4)若要刪除某個資源,應該使用DELETE 方法。

三、HTTP請求的方法

(1)GET:經過請求URI獲得資源
(2)POST:用於添加新的內容
(3)PUT:用於修改某個內容,若不存在則添加
(4)DELETE:刪除某個內容
(5)OPTIONS :詢問能夠執行哪些方法
(6)HEAD :相似於GET, 可是不返回body信息,用於檢查對象是否存在,以及獲得對象的元數據
(7)CONNECT :用於代理進行傳輸,如使用SSL
(8)TRACE:用於遠程診斷服務器

 

 

四、HTTP請求的狀態碼

(1)成功Successful2xx:此類狀態碼標識客戶端的請求被成功接收、理解並接受。常見如200(OK)、204(NoContent)。
(2)重定向Redirection3xx:這個類別的狀態碼標識用戶代理要作出進一步的動做來完成請求。常見如301(MovedPermanently)、302(MovedTemprarily)。
(3)客戶端錯誤Client Error 4xx:4xx類別的狀態碼是當客戶端象是出錯的時使用的。常見如400(BadRequest)、401(Unauthorized)、403(Forbidden)、404(NotFound)。
(4)服務器錯誤Server Error 5xx:響應狀態碼以5開頭表示服務器知道本身出錯或者沒有能力執行請求。常見如500(InternalServer Error)、502(BadGateway)、504(GatewayTimeout)。

附HTTP1.1的標準簡介:http://blog.chinaunix.net/uid-9188830-id-2007021.html



五、RestTemplate

5.1 簡介

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.

簡單說就是:簡化了發起HTTP請求以及處理響應的過程,而且支持REST。爲何說簡化了呢?

來看兩種實現方式

(1)使用java.net包下的URLConnection創建鏈接

        String result= "";
        BufferedReaderin = null;
        try {
            String urlNameString= url +"?" + param;
            URL realUrl= new URL(urlNameString);
            // 打開和URL之間的鏈接
            URLConnectionconnection = realUrl.openConnection();
            // 設置通用的請求屬性
            connection.setRequestProperty("accept","*/*");
            connection.setRequestProperty("connection","Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 創建實際的鏈接
            connection.connect();
            // 獲取全部響應頭字段
            Map<String,List<String>> map = connection.getHeaderFields();
            // 遍歷全部的響應頭字段
            for(String key : map.keySet()) {
                System.out.println(key+ "--->" + map.get(key));
            }
            // 定義 BufferedReader輸入流來讀取URL的響應
            in =new BufferedReader(newInputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine())!= null) {
                result += line;
            }
        } catch (Exception e) {
            …
        }
        // 使用finally塊來關閉輸入流
        finally{
         // 關閉流
        }

(2)使用RestTempalte

ResponseEntity<SsoUrlPrm>result = restTemplate.getForEntity(requestPathUrl,SsoUrlPrm.class);  

5.2 對外開放的接口

(1)

 

DELETE delete
GET getForObject
  getForEntity
HEAD headForHeaders
OPTIONS optionsForAllow
POST postForLocation
  postForObject
PUT put
any exchange
  execute

 

(2)每個小類又分三種,這三種有什麼區別?

* 第一種和第二種的首個參數都是用String表示一個URI。但它們的最後一個參數分別是Object[]和Map
* 第三種的首個參數使用java.net.URI表示一個URI。且只有兩個參數

  這是由於,String類型的URI支持佔位符。好比:

restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}",String.class,"42", "21");

那麼最終訪問的URI爲:http://example.com/hotels/42/bookings/21

  可是String有一個小缺陷:String形式的URI會被URL編碼兩次(URL encode請自行百度),這就要求服務器在獲取URI中的參數時主動進行一次解碼,但若是服務的提供者不這麼作呢?

  這時就須要使用不會使用任何編碼的java.net.URI

 

PS:參數‘Class<T>  responseType’定義了返回數據的類型。

(3)Exchange

  與其它接口的不一樣:

>容許調用者指定HTTP請求的方法(GET,POST,PUT等)

>能夠在請求中增長body以及頭信息,其內容經過參數‘HttpEntity<?>requestEntity’描述

>exchange支持‘含參數的類型’(即泛型類)做爲返回類型,該特性經過‘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());

獲得的結果是:

class java.util.ArrayList
java.util.AbstractList<E>
java.util.ArrayList<java.lang.String>
* 這是由於ParameterizedTypeReference<ArrayList<String>>並不根據實參而是使用getGenericSuperclass()方法獲取其父類的類型(注意這裏的new有花括號,是ParameterizedTypeReference的子類),父類的類型經過 java. lang. reflect.Type描述,而後經過Type的getActualTypeArguments()得到了父類的實參類型,注意獲得的Type類,並非class類。

(4)excute

全部的get、post、delete、put、options、head、exchange方法最終調用的都是excute方法。舉個栗子:
@Override
public <T> T getForObject(String url, Class<T>     responseType, Object... urlVariables) throws     RestClientException {
  RequestCallback requestCallback =     acceptHeaderRequestCallback(responseType);
  HttpMessageConverterExtractor<T> responseExtractor =
<span style="white-space:pre">    </span>new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
  return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables);
}
Excute方法只是將String格式的URI轉成了java.net.URI,以後調用了doExecute方法。整個調用過程

6.doExcute

6.1 定義
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,ResponseExtractor<T> responseExtractor) throws RestClientException {…}

 

這裏須要瞭解兩個類: RequestCallback &ResponseExtractor
 
6.2 RequestCallback
Callback interface for code that operates on a ClientHttpRequest. Allows to manipulate the request headers, and write to the request body.
簡單說:用於操做請求頭和body,在請求發出前執行。

該接口有兩個實現類:
AcceptHeaderRequestCallback 只處理請求頭,用於getXXX()方法。
HttpEntityRequestCallback 繼承於AcceptHeaderRequestCallback能夠處理請求頭和body,用於putXXX()、postXXX()和exchange()方法。

* DELETE、HEAD、OPTIONS沒有使用這個接口。
 
6.3 發起請求
 
6.4 ResponseExtractor
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.
簡單說:解析HTTP響應的數據,並且不須要擔憂異常和資源的關閉。
 
該接口有三個實現類:
HeadersExtractor 用於提取請求頭。
HttpMessageConverterExtractor 用於提取響應body。
ResponseEntityResponseExtractor 使用HttpMessageConverterExtractor提取body(委託模式),而後將body和響應頭、狀態封裝成ResponseEntity對象。
6.4.2 提取響應body
提取分三步:
(1)提取器HttpMessageConverterExtractor尋找可用的轉化器
在默認的RestTemplate的構造函數中初始化了轉化器集合,包括:

轉化器

可轉化的類型
ByteArrayHttpMessageConverter byte[]
StringHttpMessageConverter String
ResourceHttpMessageConverter Resource
SourceHttpMessageConverter javax.xml.transform.*
AllEncompassingFormHttpMessageConverter MultiValueMap
Jaxb2RootElementHttpMessageConverter XmlRootElement,XmlType(註解)
...  
MappingJackson2HttpMessageConverter Json
除了前五個,其餘的轉化器會由classloader嘗試加載某個類來判斷工程是否包含某個包,然後決定是否加入轉化器集合。
提取器遍歷轉化器集合以查找可用的轉化器,其中MappingJackson2HttpMessageConverter老是在最後一個,由於該類實現了GenericHttpMessageConverter,算是一個通用轉化器,只有在找不到合適的轉化器時才輪到它。Spring提供了一個該類的實現,以保證老是能獲得該類。

(2)轉化器尋找可用的反序列化器
轉化器持有一個反序列化器緩存集合,首先從緩存中尋找
若是已有可用的反序列化器,則直接返回。不然建立一個新的反序列化器。
 
反序列化器保存着待反序列化類的域、方法、構造器等信息,反序列化時就是使用構造器建立了一個新的實例。
以jackson爲例,建立反序列化器的過程在jackson-databind-xxx.jar中,有興趣的能夠看一下。調用棧以下(由下往上找):
BeanDeserializerFactory.addBeanProps/addObjectIdReader/addReferenceProperties/addInjectables
BeanDeserializerFactory.buildBeanDeserializer
BeanDeserializerFactory.createBeanDeserializer

(3)反序列化器執行反序列化

TOKEN

Json的一個或一組字符
START_OBJECT {
END_OBJECT }
START_ARRAY [
END_ARRAY ]
VALUE_TRUE true
VALUE_FALSE false
...  

調用棧:

相關文章
相關標籤/搜索