激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 關于RestTemplate的使用深度解析

關于RestTemplate的使用深度解析

2022-02-26 00:37編走編想 Java教程

這篇文章主要介紹了對RestTemplate的深度解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

從 Spring 4.3 開始加入了 OkHttp3ClientHttpRequestFactory

一、概述

本文主要介紹 Spring Web 模塊中的 RestTemplate 組件的原理、優缺點、以及如何擴展以滿足各種需求。

在介紹 RestTemplate 之前,我們先來談談 HTTP Client,談談選擇一個優秀的 HTTP Client 實現的的重要性,以及一個優秀的 HTTP Client 應該具備哪些特性。

在 Java 社區中,HTTP Client 主要有 JDK 的 HttpURLConnection、Apache Commons HttpClient(或被稱為 Apache HttpClient 3.x)、Apache HttpComponents Client(或被稱為 Apache HttpClient 4.x)、Square 公司開源的 OkHttp。

除了這幾個純粹的 HTTP Client 類庫以外,還有 Spring 的 RestTemplate、Square 公司的 Retrofit、Netflix 公司的 Feign,以及像 Apache CXF 中的 client 組件。這些框架和類庫主要是針對 Web Service 場景,尤其是 RESTful Web Service。它們往往是基于前面提到的 HTTP Client 實現,并在其基礎上提供了消息轉換、參數映射等對于 Web Service 來說十分必要的功能。

(當然,像 Netty、Mina 這樣的網絡 IO 框架,實現 HTTP 自然也不再話下,但這些框架通常過于底層,不會被直接使用)

選擇一個優秀的 HTTP Client 的重要性

雖然現在服務間的調用越來越多地使用了 RPC 和消息隊列,但是 HTTP 依然有適合它的場景。

RPC 的優勢在于高效的網絡傳輸模型(常使用 NIO 來實現),以及針對服務調用場景專門設計協議和高效的序列化技術。而 HTTP 的優勢在于它的成熟穩定、使用實現簡單、被廣泛支持、兼容性良好、防火墻友好、消息的可讀性高。所以在開放 API、跨平臺的服務間調用、對性能要求不苛刻的場景中有著廣泛的使用。

正式因為 HTTP 存在著很廣泛的應用場景,所以選擇一個優秀的 HTTP Client 便是十分重要的。

優秀的 HTTP Client 需要具備的特性

  • 連接池
  • 超時時間設置(連接超時、讀取超時等)
  • 是否支持異步
  • 請求和響應的編解碼
  • 可擴展性

連接池

因為目前 HTTP 1.1 不支持多路復用,只有 HTTP Pipeline 這用半復用的模型支持。所以,在需要頻繁發送消息的場景中,連接池使必須支持的,以減少頻繁建立連接所帶來的不必要的性能損耗。

超時時間設置(連接超時、讀取超時等)

當對端出現問題的時候,長時間的,甚至是無限的超時等待是絕對不能接受的。所以必須必須能夠設置超時時間。

是否支持異步

HTTP 相關技術(服務器端和客戶端)通常被人認為是性能低下的一個重要原因在于,在很長一段時間里,HTTP 的相關實現缺乏對異步的支持。這不僅指非阻塞 IO,也包括異步的編程模型。缺乏異步編程模型的后果就是,即便 HTTP 協議棧是基于非阻塞 IO 實現的,調用客戶端的或者在服務端處理消息的線程有大量時間被浪費在了等待 IO 上面。所以,異步是非常重要的特性。

請求和響應的編解碼

通常,開發人員希望面向對象使用各種服務(這里面自然也包括基于 HTTP 協議的服務),而不是直接面對原始的消息和響應開發。所以,透明地將 HTTP 請求和響應進行編解碼是十分有必要,因為這可以很大程度地降低開發人員的工作量。

可擴展性

不論一個框架設計的多好,總有一些特殊場景是它們無法原生支持的。這時可擴展性的好壞便體現出來了。

答案

基于上述幾點的考慮,RestTemplate 是相對好的選擇。原因在于 RestTemplate 本身基于成熟的 HTTP Client 實現(Apache HttpClient、OkHttp 等),并可以靈活地在這些實現中切換,而且具有良好的擴展性。最重要的是提供了前面幾個 HTTP Client 不具備的消息編解碼能力。

這里要提一句為什么沒有自己封裝 HTTP Client 的原因。這個原因在于想要基于一種 HTTP Client 去提供消息編解碼能力和一定的擴展能力并不難,但是如果要設計出一個通用的,對底層實現透明的,具有優秀如 Spring 的擴展性設計的框架并不是一件容易事。這里的不易并不在于技術有多高深,而是在于優秀的擴展性設計往往源自從眾多優秀程序員、社區和軟件公司得到的豐富的一線實踐經驗,再由像 Spring 轉換為最終設計。這樣的產品不是一朝一夕就能得到的。在我們覺得自己打造自己的工具之前,我們可以先深入了解現有的優秀功能都能做到什么。

二、使用 RestTemplate 的缺點

欲揚先抑,我們先來看加入使用 RestTemplate,可能會遇到哪些“坑”。

依賴 Spring 其它模塊

雖然 spring-web 模塊對其它 Spring 模塊并沒有顯式的依賴(Maven dependency 的 scope 為 compile),但是對于一些功能,比如異步版本的 RestTemplate,要求必須有 4.1 以上版本的 spring-core 模塊。

所以,要想 RestTemplate 完全發揮其功能,最好能有相近版本的其它的 Spring 模塊相配合(spring-core、spring-context、spring-beans、spring-aop)

默認情況下 RestTemplate 存在的不足

Spring Web 模塊中的 RestTemplate 是一個很不錯的面向 RESTful Web 服務的客戶端。它提供了很多簡化對 RESTful Web 服務調用的功能,例如 Path Parameter 的格式化功能(/hotels/{hotel_id}/books/{book_id},這里的 hotel_id 和 book_id 就是 Path Paramter)、JSON 或 XML 等格式的數據與實體類之間的透明轉換等。

所謂默認情況指的是不去擴展 RestTemplate 所提供的類或接口,而是完全依賴其本身提供的代碼。在這種情況下,RestTemplate 還是有一些不便的地方。例如,它的 Path Parameter 格式化功能,對于普通 HTTP 服務的調用來說,反而成為了一個缺點,因為普通的 HTTP 服務的 GET 方法常使用 Query Parameter,而不是 Path Parameter。Query Paramter 的形式是 an_http_url?name1=value1&name2=value2。例如 getOrder.action?order_code=xxx。如果使用 RestTemplate,作為參數傳遞給 RestTemplate 的 URL 就必須是 getOrder.action?order_code={order_code}。如果是固定的參數還好,如果一個 HTTP 服務的 Query Parameter 是可變的,那就很不方便了。

三、擴展 RestTemplate

注意,下面涉及到的代碼都是基于 spring-web 4.2.6.RELEASE 版本

設置 Query Params

上面提到,RestTemplate 的 getForEntity、getForObject、postForEntity 等方法中的 Map 參數是 uriVariables,即我們常說的 Path Param,而非 Query Param(這兩個參數的定義可以參照 JAX-RS 中 @PathParam 和 @QueryParam 的定義)。

Path Param 是 URL 的一部分,RESTful 的 Web Service 會按照其定義的 URL Template 從 URL 中解析出其對應的值

RestTemplate 的這種機制面對 RESTful 的 Web Service 無疑是方便的,但很多情況下我們還是希望 RestTemplate 能夠在開發人員不用編寫額外代碼的情況下將 Map 類型的參數當做 Query Param 發送給對端的服務。

幸好來自 Spring 大家庭的 RestTemplate 也具有良好的可擴展性,其具有一個名為 UriTemplateHandler 擴展點。因為不論是 Path Param 還是 Query Param,它們都是 URI 的一部分,所以只需實現自定義的 URI 生成機制即可解決這個問題。

通過擴展 DefaultUriTemplateHandler,我們可以將 Map<String, ?> uriVariables 也作為 Query Param。具體實現如下:

?
1
2
3
4
5
6
7
8
9
10
11
public class QueryParamsUrlTemplateHandler extends DefaultUriTemplateHandler {
    @Override
    public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
        UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(uriTemplate);
        for (Map.Entry<String, ?> varEntry : uriVariables.entrySet()) {
            uriComponentsBuilder.queryParam(varEntry.getKey(), varEntry.getValue());
        }
        uriTemplate = uriComponentsBuilder.build().toUriString();
        return super.expand(uriTemplate, uriVariables);
    }
}

上面的實現基于 DefaultUriTemplateHandler,所以保有了原來設置 Path Param 的功能。

設置自定義的 HTTP Header

實現這個需求有多種方法,比如通過攔截器。這里使用另一個方法,通過一個自定義的 ClientHttpRequestFactory

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CustomHeadersClientHttpRequestFactoryWrapper extends AbstractClientHttpRequestFactoryWrapper {
    private HttpHeaders customHeaders = new HttpHeaders();
    /**
     * Create a {@code AbstractClientHttpRequestFactoryWrapper} wrapping the given request factory.
     *
     * @param requestFactory the request factory to be wrapped
     */
    protected CustomHeadersClientHttpRequestFactoryWrapper(ClientHttpRequestFactory requestFactory) {
        super(requestFactory);
    }
    @Override
    protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod,
            ClientHttpRequestFactory requestFactory) throws IOException {
        ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
        for (Map.Entry<String, List<String>> headerEntry : customHeaders.entrySet()) {
            request.getHeaders().put(headerEntry.getKey(), headerEntry.getValue());
        }
        return request;
    }
    public void addHeader(String header, String... values) {
        customHeaders.put(header, Arrays.asList(values));
    }
}

簡化配置

RestTemplate 提供了良好的擴展性,但是有些設置是使用 ``

四、RestTemplate 原理解析

HTTP Client 實現

RestTemplate 本身并沒有做 HTTP 底層的實現,而是利用了現有的技術,如 JDK 或 Apache HttpClient 等。

RestTemplate 需要使用一個實現了 ClientHttpRequestFactory 接口的類為其提供 ClientHttpRequest 實現(另外還有 AsyncClientHttpRequestFactory 對應于異步 HTTP 實現,這里暫且不表)。而 ClientHttpRequest 則實現封裝了組裝、發送 HTTP 消息,以及解析響應的的底層細節。

目前(4.2.6.RELEASE)的 RestTemplate 主要有四種 ClientHttpRequestFactory 的實現,它們分別是:

  • 基于 JDK HttpURLConnection 的 SimpleClientHttpRequestFactory
  • 基于 Apache HttpComponents Client 的 HttpComponentsClientHttpRequestFactory
  • 基于 OkHttp 2(OkHttp 最新版本為 3,有較大改動,包名有變動,不和老版本兼容)的 OkHttpClientHttpRequestFactory
  • 基于 Netty4 的 Netty4ClientHttpRequestFactory

另外,還有用于提供攔截器功能的 InterceptingClientHttpRequestFactory。

寫消息

寫消息指的是 requestBody 轉換為某一種格式,如 JSON、XML 的數據的過程。

spring-web 模塊提供了一個 HttpMessageConverter 接口,用來讀寫 HTTP 消息。這個接口不僅被 RestTemplate 使用,也被 Spring MVC 所使用。

spring-web 模塊提供了基于 Jackson、GSON 等類庫的 HttpMessageConverter,用于進行 JSON 或 XML 格式數據的轉換。

RestTemplate 在發送消息時,會根據消息的 ContentType 或者 RequestBody 對象本身的一些屬性判斷究竟是使用哪個 HttpMessageConverter 寫消息。

具體來說,如果 RequestBody 是一個 HttpEntity 的話,會從中讀取 ContentType 屬性。同時,RequestBody 對象本身也會覺得一個 HttpMessageConverter 是否會處理這個對象。例如,ProtobufHttpMessageConverter 會要求 RequestBody 對象必須實現 com.google.protobuf.Message 接口。

讀消息

讀消息指的是讀取 HTTP Response 中的數據,轉換為用戶指定的格式(通過 Class<T> responseType 參數指定)。類似于寫消息的處理,讀消息的處理也是通過 ContentType 和 responseType 來選擇的相應 HttpMessageConverter 來進行的。

錯誤處理

RestTemplate 提供了一個 ResponseErrorHandler 的接口,用來處理錯誤的 Response。可以通過設置自定義的 ResponseErrorHandler 來實現擴展。

后記

根據我上面表達的思想,一個統一、規范和簡化 RestTemplate 使用的工具已經產生,不過暫時由于其代碼是公司項目的一部分,所以暫時不便公開。而且我希望是在這個工具經過了更多的實踐考驗之后再貢獻出來會更好。

目前的一個完整使用案例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class SpringConfigurationDemo {
    @Bean
    public RestTemplate myRestTemplate() {
        return RestTemplateBuilder.create()
                .withClientKey("myRestTemplate")
                .implementation(HttpClientImplementation.OK_HTTP)
                .clearMessageConverters()
                .setMessageConverter(new MappingJackson2HttpMessageConverter(), MediaType.TEXT_PLAIN)
                .enableAutoQueryParams()
                .connectTimeout(100)
                .readTimeout(200)
                .header(HttpHeaders.USER_AGENT, "MyAgent")
                .build();
    }
}

雖然 RestTemplate 是一個很不錯的 HTTP Client,但 Netflix 已經開源了一個更好地 HTTP Client 工具 - Feign。它是一個聲明式的 HTTP Client,在易用性、可讀性等方面大幅領先于現有的工具。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://my.oschina.net/lifany/blog/688889

延伸 · 閱讀

精彩推薦
  • Java教程SpringBoot個性化配置的方法步驟

    SpringBoot個性化配置的方法步驟

    這篇文章主要介紹了SpringBoot個性化配置的方法步驟,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    吉林烏拉9352021-07-14
  • Java教程Java單例模式繼承覆蓋多態原理詳解

    Java單例模式繼承覆蓋多態原理詳解

    這篇文章主要介紹了Java單例模式繼承覆蓋多態原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可...

    愛笑的berg5142020-09-29
  • Java教程簡單談談Java垃圾回收

    簡單談談Java垃圾回收

    本文是看了James Gosling的<>后結合自己的一些項目經驗,簡單總結下關于java的垃圾回收問題的看法,有需要的小伙伴可以參考下 ...

    weixueyuan2962020-05-04
  • Java教程Spring思維導圖助你輕松學習Spring

    Spring思維導圖助你輕松學習Spring

    這篇文章主要為大家詳細介紹了Spring思維導圖,幫助你輕松學習Spring的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    java思維導圖11862021-04-14
  • Java教程scala 操作數據庫的方法

    scala 操作數據庫的方法

    這篇文章主要介紹了scala 操作數據庫的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著...

    張樂19936492019-06-29
  • Java教程后端Java開發如何防御XSS攻擊

    后端Java開發如何防御XSS攻擊

    跨站腳本攻擊(XSS)可以讓攻擊者在受害者的瀏覽器中執行惡意腳本來修改網頁內容、將用戶重定向到非法網站、偽造用戶登錄態、竊取用戶的隱私信息、...

    碼農小胖哥5592021-06-30
  • Java教程詳解SpringBoot之訪問靜態資源(webapp...)

    詳解SpringBoot之訪問靜態資源(webapp...)

    這篇文章主要介紹了詳解SpringBoot之訪問靜態資源(webapp...),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    臨窗,聽雨聲8312020-09-15
  • Java教程Mybatis #foreach中相同的變量名導致值覆蓋的問題解決

    Mybatis #foreach中相同的變量名導致值覆蓋的問題解決

    本文主要介紹了Mybatis #foreach中相同的變量名導致值覆蓋的問題解決,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    liupjie9612021-10-29
主站蜘蛛池模板: 成人午夜视频免费看 | 国产精品午夜一区 | av成人在线免费观看 | 亚洲午夜视频 | 久草在线视频网 | 亚洲天堂在线电影 | 国产精品久久久久久238 | 欧美一级做一a做片性视频 黄色网址免费进入 | 国产午夜精品一区二区三区免费 | 国产精品入口夜色视频大尺度 | 主人在调教室性调教女仆游戏 | 久久久三级免费电影 | av在线播放免费观看 | 曰韩黄色片 | 91成人亚洲 | 国产乱淫av片免费观看 | 伦一区二区三区中文字幕v亚洲 | 美国黄色小视频 | 777sesese| 一级尻逼视频 | 国产成人av在线播放 | 国产人成精品综合欧美成人 | 国产精品亚洲激情 | 美女毛片在线观看 | 91久久精品一二三区 | 亚洲一区二区三区四区精品 | 欧美国产成人在线 | 久久精品电影网 | 久久国产一级 | 深夜精品福利 | 久久久久久69 | 久久999精品久久久 国产噜噜噜噜久久久久久久久 | 亚洲午夜精选 | 最新福利在线 | 88xx成人精品视频 | 久久99精品久久久久久236 | 欧美国产免费 | 大胆在线日本aⅴ免费视频 美国黄色毛片女人性生活片 | 婷婷久久久久久 | 538任你躁在线精品视频网站 | 一区二区三区欧洲 |