欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于RestTemplate的使用深度解析

 更新時(shí)間:2021年10月23日 11:41:52   作者:編走編想  
這篇文章主要介紹了對RestTemplate的深度解析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

從 Spring 4.3 開始加入了 OkHttp3ClientHttpRequestFactory

一、概述

本文主要介紹 Spring Web 模塊中的 RestTemplate 組件的原理、優(yōu)缺點(diǎn)、以及如何擴(kuò)展以滿足各種需求。

在介紹 RestTemplate 之前,我們先來談?wù)?HTTP Client,談?wù)勥x擇一個(gè)優(yōu)秀的 HTTP Client 實(shí)現(xiàn)的的重要性,以及一個(gè)優(yōu)秀的 HTTP Client 應(yīng)該具備哪些特性。

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

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

(當(dāng)然,像 Netty、Mina 這樣的網(wǎng)絡(luò) IO 框架,實(shí)現(xiàn) HTTP 自然也不再話下,但這些框架通常過于底層,不會被直接使用)

選擇一個(gè)優(yōu)秀的 HTTP Client 的重要性

雖然現(xiàn)在服務(wù)間的調(diào)用越來越多地使用了 RPC 和消息隊(duì)列,但是 HTTP 依然有適合它的場景。

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

正式因?yàn)?HTTP 存在著很廣泛的應(yīng)用場景,所以選擇一個(gè)優(yōu)秀的 HTTP Client 便是十分重要的。

優(yōu)秀的 HTTP Client 需要具備的特性

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

連接池

因?yàn)槟壳?HTTP 1.1 不支持多路復(fù)用,只有 HTTP Pipeline 這用半復(fù)用的模型支持。所以,在需要頻繁發(fā)送消息的場景中,連接池使必須支持的,以減少頻繁建立連接所帶來的不必要的性能損耗。

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

當(dāng)對端出現(xiàn)問題的時(shí)候,長時(shí)間的,甚至是無限的超時(shí)等待是絕對不能接受的。所以必須必須能夠設(shè)置超時(shí)時(shí)間。

是否支持異步

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

請求和響應(yīng)的編解碼

通常,開發(fā)人員希望面向?qū)ο笫褂酶鞣N服務(wù)(這里面自然也包括基于 HTTP 協(xié)議的服務(wù)),而不是直接面對原始的消息和響應(yīng)開發(fā)。所以,透明地將 HTTP 請求和響應(yīng)進(jìn)行編解碼是十分有必要,因?yàn)檫@可以很大程度地降低開發(fā)人員的工作量。

可擴(kuò)展性

不論一個(gè)框架設(shè)計(jì)的多好,總有一些特殊場景是它們無法原生支持的。這時(shí)可擴(kuò)展性的好壞便體現(xiàn)出來了。

答案

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

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

二、使用 RestTemplate 的缺點(diǎn)

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

依賴 Spring 其它模塊

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

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

默認(rèn)情況下 RestTemplate 存在的不足

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

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

三、擴(kuò)展 RestTemplate

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

設(shè)置 Query Params

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

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

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

幸好來自 Spring 大家庭的 RestTemplate 也具有良好的可擴(kuò)展性,其具有一個(gè)名為 UriTemplateHandler 擴(kuò)展點(diǎn)。因?yàn)椴徽撌?Path Param 還是 Query Param,它們都是 URI 的一部分,所以只需實(shí)現(xiàn)自定義的 URI 生成機(jī)制即可解決這個(gè)問題。

通過擴(kuò)展 DefaultUriTemplateHandler,我們可以將 Map<String, ?> uriVariables 也作為 Query Param。具體實(shí)現(xiàn)如下:

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);
    }
}

上面的實(shí)現(xiàn)基于 DefaultUriTemplateHandler,所以保有了原來設(shè)置 Path Param 的功能。

設(shè)置自定義的 HTTP Header

實(shí)現(xiàn)這個(gè)需求有多種方法,比如通過攔截器。這里使用另一個(gè)方法,通過一個(gè)自定義的 ClientHttpRequestFactory

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 提供了良好的擴(kuò)展性,但是有些設(shè)置是使用 ``

四、RestTemplate 原理解析

HTTP Client 實(shí)現(xiàn)

RestTemplate 本身并沒有做 HTTP 底層的實(shí)現(xiàn),而是利用了現(xiàn)有的技術(shù),如 JDK 或 Apache HttpClient 等。

RestTemplate 需要使用一個(gè)實(shí)現(xiàn)了 ClientHttpRequestFactory 接口的類為其提供 ClientHttpRequest 實(shí)現(xiàn)(另外還有 AsyncClientHttpRequestFactory 對應(yīng)于異步 HTTP 實(shí)現(xiàn),這里暫且不表)。而 ClientHttpRequest 則實(shí)現(xiàn)封裝了組裝、發(fā)送 HTTP 消息,以及解析響應(yīng)的的底層細(xì)節(jié)。

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

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

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

寫消息

寫消息指的是 requestBody 轉(zhuǎn)換為某一種格式,如 JSON、XML 的數(shù)據(jù)的過程。

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

spring-web 模塊提供了基于 Jackson、GSON 等類庫的 HttpMessageConverter,用于進(jìn)行 JSON 或 XML 格式數(shù)據(jù)的轉(zhuǎn)換。

RestTemplate 在發(fā)送消息時(shí),會根據(jù)消息的 ContentType 或者 RequestBody 對象本身的一些屬性判斷究竟是使用哪個(gè) HttpMessageConverter 寫消息。

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

讀消息

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

錯(cuò)誤處理

RestTemplate 提供了一個(gè) ResponseErrorHandler 的接口,用來處理錯(cuò)誤的 Response??梢酝ㄟ^設(shè)置自定義的 ResponseErrorHandler 來實(shí)現(xiàn)擴(kuò)展。

后記

根據(jù)我上面表達(dá)的思想,一個(gè)統(tǒng)一、規(guī)范和簡化 RestTemplate 使用的工具已經(jīng)產(chǎn)生,不過暫時(shí)由于其代碼是公司項(xiàng)目的一部分,所以暫時(shí)不便公開。而且我希望是在這個(gè)工具經(jīng)過了更多的實(shí)踐考驗(yàn)之后再貢獻(xiàn)出來會更好。

目前的一個(gè)完整使用案例如下:

@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 是一個(gè)很不錯(cuò)的 HTTP Client,但 Netflix 已經(jīng)開源了一個(gè)更好地 HTTP Client 工具 - Feign。它是一個(gè)聲明式的 HTTP Client,在易用性、可讀性等方面大幅領(lǐng)先于現(xiàn)有的工具。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決springboot引入swagger2不生效問題

    解決springboot引入swagger2不生效問題

    這篇文章主要為大家介紹了解決springboot引入swagger2不生效問題的方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • springboot集成redis哨兵集群的實(shí)現(xiàn)示例

    springboot集成redis哨兵集群的實(shí)現(xiàn)示例

    本文主要介紹了springboot集成redis哨兵集群的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-08-08
  • java中關(guān)于控件JTextArea的幾個(gè)方法

    java中關(guān)于控件JTextArea的幾個(gè)方法

    這篇文章主要介紹了java中關(guān)于控件JTextArea的幾個(gè)方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • java面試突擊之sleep和wait有什么區(qū)別詳析

    java面試突擊之sleep和wait有什么區(qū)別詳析

    按理來說sleep和wait本身就是八竿子打不著的兩個(gè)東西,但是在實(shí)際使用中大家都喜歡拿他們來做比較,或許是因?yàn)樗鼈兌伎梢宰尵€程處于阻塞狀態(tài),這篇文章主要給大家介紹了關(guān)于java面試突擊之sleep和wait有什么區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • springboot+feign+Hystrix整合(親測有效)

    springboot+feign+Hystrix整合(親測有效)

    本文主要介紹了springboot+feign+Hystrix整合,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-11-11
  • 手把手教你搭建SpringMVC框架——最小化配置

    手把手教你搭建SpringMVC框架——最小化配置

    這篇文章主要介紹了手把手教你搭建SpringMVC框架——最小化配置,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-02-02
  • java打jar包的幾種方式詳解

    java打jar包的幾種方式詳解

    這篇文章主要介紹了java打jar包的幾種方式,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-11-11
  • IDEA的spring項(xiàng)目使用@Qualifier飄紅問題及解決

    IDEA的spring項(xiàng)目使用@Qualifier飄紅問題及解決

    這篇文章主要介紹了IDEA的spring項(xiàng)目使用@Qualifier飄紅問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • shell腳本運(yùn)行java程序jar的方法

    shell腳本運(yùn)行java程序jar的方法

    本篇文章主要介紹了shell腳本運(yùn)行java程序jar的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • 解決Eclipse配置Tomcat出現(xiàn)Cannot create a server using the selected type錯(cuò)誤

    解決Eclipse配置Tomcat出現(xiàn)Cannot create a server using the selected

    這篇文章主要介紹了解決Eclipse配置Tomcat出現(xiàn)Cannot create a server using the selected type錯(cuò)誤的相關(guān)資料,需要的朋友可以參考下
    2017-02-02

最新評論