RestTemplate源碼剖析:Encode

背景

業(yè)務(wù)方經(jīng)過某些業(yè)務(wù)處理之后寥袭,帶過來了一系列參數(shù)。其中关霸,有個(gè)url參數(shù)供回調(diào)使用传黄,但這個(gè)url經(jīng)業(yè)務(wù)方處理后很特殊,帶過來回調(diào)時(shí)頻頻出錯(cuò)队寇,無法正潮礻回調(diào)給業(yè)務(wù)方。于是英上,通過分析部分源碼炭序,剖析一下回調(diào)的流程啤覆。

編碼 解碼

說到url的回調(diào)苍日,很容易聯(lián)想到decode、encode窗声,即編碼解碼操作相恃。

通常來說,需要編碼操作即意味著不適合直接傳輸笨觅,某些字符可能有歧義拦耐,如中文、特殊字符等等见剩。
而通過編碼之后(如 utf-8)杀糯,傳輸給服務(wù)端,服務(wù)端自行解碼苍苞,便不會(huì)出現(xiàn)一系列亂碼問題固翰。

說明

按照如上所說狼纬,業(yè)務(wù)方帶過來了處理后的url,另附上說明骂际,url無需解碼疗琉,直接回調(diào)即可,但事情往往沒那么順利歉铝。不管是使用spring5 webclient盈简,還是restTemplate以get請(qǐng)求回調(diào)業(yè)務(wù)方,均會(huì)出錯(cuò)太示。由于最終使用了restTemplate解決了問題柠贤,對(duì)此本次剖析一下restTemplate。

注:WebClient同樣可以解決問題类缤,剖析restTemplate的因由在于綜合分析了項(xiàng)目的引用和耦合度种吸,不適合改動(dòng)webclient模塊,故而拓展了restTemplate呀非。

剖析

  1. 先寫個(gè)極其簡(jiǎn)單的單測(cè)入口坚俗,進(jìn)行RestTemplate的源碼分析。


  1. 緊接著集中分析RestTemplate類
  • 請(qǐng)求頭岸裙、請(qǐng)求體是由RequestCallback來處理的猖败,請(qǐng)求結(jié)果是由ResponseExtractor來處理。


  • RestTemplate默認(rèn)已經(jīng)初始化添加了許多對(duì)象讀寫轉(zhuǎn)換器降允。


  1. 進(jìn)入execute這個(gè)重要的方法繼續(xù)分析恩闻。


  1. 可以看出,先用UriTemplateHandler構(gòu)建URI剧董,再走關(guān)鍵邏輯幢尚。
  • UriTemplateHandler build URI


  • 通過構(gòu)造函數(shù)的初始化,可以看到使用的是默認(rèn)builder工廠



  • 從類繼承圖可以看出DefaultUriBuilderFactory實(shí)現(xiàn)了UriBuilderFactory翅楼,繼承了UriTemplateHandler


  • expand(String var1, Object... var2)是UriTemplateHandler接口的方法


  1. 繼續(xù)分析UriTemplateHandler接口expand方法源碼



  • 到這里我們可以發(fā)現(xiàn)DefaultUriBuilderFactory默認(rèn)builder工廠存在一個(gè)內(nèi)部類DefaultUriBuilder


  • 接著進(jìn)入分析一下UriComponentsBuilder類的源碼尉剩,從類名可以猜想到這個(gè)UriComponentsBuilder構(gòu)建器可能用于創(chuàng)建UriComponents實(shí)例


  • 顧名思義,UriComponents包含了構(gòu)成URI的組件毅臊,以及一系列的getter and setter方法


  • 繼續(xù)著剛才初始化UriComponentsBuilder的方法


  • 符合了內(nèi)置的url pattern之后繼續(xù)構(gòu)建UriComponentsBuilder理茎,通過構(gòu)造器可以看出默認(rèn)字符集是utf-8



  • 可以清晰看出初始化之后的構(gòu)建器


  1. 接下來出來外層的包裹,分析build方法


  • 分析DefaultUriBuilderFactory build的過程
    劃重點(diǎn)9苕摇T砹帧!
    因?yàn)閡riVariableValues為空蚯撩,不關(guān)心這個(gè)expand方法础倍,此時(shí)注意build方法。

  • 可以看到胎挎,build UriComponents沟启,有個(gè)encoded參數(shù)扰楼,默認(rèn)為false
    先不透露具體細(xì)節(jié),只需先記住構(gòu)建這次請(qǐng)求的UriComponents美浦,encoded參數(shù)為falseO依怠!浦辨!

  • 接著看encoded參數(shù)為false蹬竖,會(huì)影響到哪些參數(shù)值
    由encodedfalse可得到UriComponentsBuilder.EncodingHint為NONE

public UriComponents build(boolean encoded) {
      return this.buildInternal(encoded ? UriComponentsBuilder.EncodingHint.FULLY_ENCODED : (this.encodeTemplate ? UriComponentsBuilder.EncodingHint.ENCODE_TEMPLATE : UriComponentsBuilder.EncodingHint.NONE));
}
  • 再接著就是構(gòu)造HierarchicalUriComponents


HierarchicalUriComponents uric = new HierarchicalUriComponents(this.scheme, this.fragment, this.userInfo, this.host, this.port, this.pathBuilder.build(), this.queryParams, hint == UriComponentsBuilder.EncodingHint.FULLY_ENCODED);
  • 最后可以看到HierarchicalUriComponents.EncodeState為RAW
    這里劃重點(diǎn)!A鞒辍币厕!
  1. 繼續(xù)看外層的包裹,構(gòu)建完之后的UriComponents


  • 繼續(xù)分析createUri芽腾,由于匹配到是默認(rèn)的URI_COMPONENT旦装,則走encode方法


  • 本次剖析很重要的一個(gè)方法,終于要接近問題的真相了L稀R蹙睢!

  • 從上個(gè)提示重要的剖析點(diǎn)得知艰躺,構(gòu)建UriComponents呻袭,encoded參數(shù)默認(rèn)為false,進(jìn)而從源碼可得知腺兴,HierarchicalUriComponents.EncodeState的值為RAW左电。那么就會(huì)走下面的encodeUriComponent的方法(至于怎么encode暫不在分析范圍內(nèi),看源碼貌似是用字節(jié)流操作來encode页响,有興趣可以閱讀)


  • 接著對(duì)segment篓足,queryParams等進(jìn)行編碼


  • 參數(shù)編碼的細(xì)節(jié),勾出來的是Java8的Function闰蚕,實(shí)現(xiàn)就不貼了栈拖,源碼比較簡(jiǎn)單


  • 最終,通過比對(duì)參數(shù)ext_info的值陪腌,便得知參數(shù)被主動(dòng)encode了


解決方案

ok~~~
分析完后辱魁,知道問題的根源在不該被encode的URL卻在restTemplate的深處被動(dòng)encode了烟瞧,因此導(dǎo)致了回調(diào)的失敗诗鸭。既然明確問題所在,也分析過源碼参滴,解決起來就很簡(jiǎn)單了强岸。如下:

構(gòu)建UriComponentsBuilder,builder構(gòu)建時(shí)傳入encoded值為true砾赔,并轉(zhuǎn)URI形式入?yún)Ⅱ蚬浚瑔栴}解決青灼。


枚舉狀態(tài)總結(jié)

為了幫助大家記憶,簡(jiǎn)單理一下狀態(tài)妓盲。

  1. UriComponentsBuilder.EncodingHint

UriComponentsBuilder構(gòu)建器有個(gè)枚舉EncodingHint杂拨,UriComponentsBuilder構(gòu)建時(shí)encoded默認(rèn)為false。

  • 若encoded為false悯衬,則看有沒有提供另外的encodeTemplate弹沽;
    若有則值為ENCODE_TEMPLATE,否則為NONE
  • 若encoded為true時(shí)筋粗,值為FULLY_ENCODED
  1. HierarchicalUriComponents.EncodeState

HierarchicalUriComponents組件有個(gè)枚舉EncodeState策橘,其值的影響在于以上的EncodingHint。
源碼的encode過濾也在于這個(gè)組件的EncodeState值D纫凇@鲆选!

  • 若EncodingHint為FULLY_ENCODED买决,則EncodeState為FULLY_ENCODED
  • 若EncodingHint不為FULLY_ENCODED(ENCODE_TEMPLATE或NONE)沛婴,則EncodeState為RAW

總結(jié)

  1. 多多閱讀源碼,只會(huì)使用還遠(yuǎn)遠(yuǎn)不夠督赤。
  2. 帶著問題閱讀源碼瘸味,是種不錯(cuò)的學(xué)習(xí)方式。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末够挂,一起剝皮案震驚了整個(gè)濱河市旁仿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孽糖,老刑警劉巖枯冈,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異办悟,居然都是意外死亡尘奏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門病蛉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來炫加,“玉大人,你說我怎么就攤上這事铺然∷仔ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵魄健,是天一觀的道長(zhǎng)赋铝。 經(jīng)常有香客問我,道長(zhǎng)沽瘦,這世上最難降的妖魔是什么革骨? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任农尖,我火速辦了婚禮区匠,結(jié)果婚禮上雏亚,老公的妹妹穿的比我還像新娘。我一直安慰自己唆香,他們只是感情好筑凫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布窟扑。 她就那樣靜靜地躺著,像睡著了一般漏健。 火紅的嫁衣襯著肌膚如雪嚎货。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天蔫浆,我揣著相機(jī)與錄音殖属,去河邊找鬼。 笑死瓦盛,一個(gè)胖子當(dāng)著我的面吹牛洗显,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播原环,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼挠唆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了嘱吗?” 一聲冷哼從身側(cè)響起玄组,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谒麦,沒想到半個(gè)月后俄讹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绕德,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年患膛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耻蛇。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踪蹬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出臣咖,到底是詐尸還是另有隱情跃捣,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布亡哄,位于F島的核電站枝缔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚊惯。R本人自食惡果不足惜愿卸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望截型。 院中可真熱鬧趴荸,春花似錦、人聲如沸宦焦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽波闹。三九已至酝豪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間精堕,已是汗流浹背孵淘。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歹篓,地道東北人瘫证。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像庄撮,于是被迫代替她去往敵國(guó)和親背捌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容