SpringCloud全面解析@FeignClient標(biāo)識(shí)接口的過程
Feign的作用
是將Http請(qǐng)求抽象化為一個(gè)Interface客戶端,可以調(diào)用接口的形式來執(zhí)行Http請(qǐng)求,以達(dá)到簡化Http調(diào)用的目的。
Feign將分散在@FeignClient,@EnableFeignClients,標(biāo)識(shí)接口,接口方法,Spring環(huán)境上的各種配置信息提取出來封裝成一個(gè)對(duì)象,然后將對(duì)象里的信息注入到RestTemplate中,生成一次Http請(qǐng)求,然后執(zhí)行。
正常在SpringMVC的Controller
是將Http請(qǐng)求的信息提取出來注入@RequestMapping標(biāo)識(shí)的方法中;而Feign是將接口中的信息提取出來,封裝成一個(gè)Http請(qǐng)求的相關(guān)信息,是對(duì)SpringMVC解析過程的一個(gè)逆向處理。
當(dāng)我們通過IOC注入接口對(duì)象時(shí),得到的肯定是此接口的實(shí)現(xiàn)類對(duì)象,這個(gè)對(duì)象應(yīng)該就是SpringCloud通過動(dòng)態(tài)代理生成的對(duì)象。對(duì)于接口對(duì)象生成動(dòng)態(tài)代理對(duì)象,一般選用JDK的Proxy,這樣實(shí)現(xiàn)簡單且耦合性低,SpringCloud就是如此。
SpringCloud將@FeignClient標(biāo)識(shí)的接口
注冊(cè)成一個(gè) FeignClientFactoryBean 類型的Bean對(duì)象,我們通過IOC注入的是此Bean的 getObject( ) 得到的對(duì)象,這篇文章主要就是講解此方法的執(zhí)行過程。
當(dāng)然直接貼源碼那太無腦了,我主要是會(huì)將解析的過程總結(jié)成一個(gè)個(gè)點(diǎn)
讓大家明白在使用過程中需要注意以及可以靈活拓展的地方
- 在解析接口前,先加載SpringCloud的Feign配置,默認(rèn)情況先加載 @FeignClient,@EnableFeignClients注解上的配置,其次加載 Spring環(huán)境里 feign.client.default 指定的配置,最后加載 feign.client.appName(應(yīng)用名稱) 指定的配置后續(xù)的配置信息會(huì)覆蓋之前的,也就是越靠后的優(yōu)先級(jí)越高??梢酝ㄟ^ “feign.client.defaultToProperties” 屬性來改變這種優(yōu)先級(jí)順序
- 驗(yàn)證:接口方法參數(shù)長度不能為0
- 驗(yàn)證:標(biāo)識(shí)@FeignClient的接口最多只能繼承一個(gè)接口
- 驗(yàn)證:@FeignClient標(biāo)識(shí)的接口的父接口不能再繼承自其它接口,也就是@FeignClient的接口最多也只能有一個(gè)上級(jí)接口
- 解析接口方法時(shí), 忽略這些類型的方法:Object的方法,Static方法,Default方法
- 將@FeignClient標(biāo)識(shí)接口的最上級(jí) Interface 的@RequestMapping注解的 value()值 設(shè)置為 MethodMetadata里的RequestTemplate的 “url” 的第一位;也就是說如果@FeignClient標(biāo)識(shí)接口 有Super Interface,那么取Super Interface 的@RequestMapping;如果沒有,那么取自己的@RequestMapping;如果都沒有此注解,那么忽略
- 如果 Method 上未標(biāo)識(shí) @RequestMapping,忽略
- Method上的@RequestMapping ,其 method()和value()的值都最多只能有一個(gè)
- 將Method上 @RequestMapping 的 value() 追加到 RequestTemplate的 “url” 上,接在 接口上的 @RequestMapping 的value() 之后,機(jī)制匹配Controller。
- 解析Method上 @RequestMapping 上的 produces(),驗(yàn)證其值只能有一個(gè)元素,將其值添加到Header上的 Accept 中,比如 Accept=application/json
- 解析 Method上@RequestMapping 上的 consumes(),驗(yàn)證其值只能有一個(gè)元素,將其值添加到Header上的 Content-Type中,比如 Content-Type=application/json
- 解析 Method上@RequestMapping 上的 headers(),headers是一個(gè)String[],其元素是一個(gè)個(gè)的鍵值對(duì),value可以使用 "${ }"來獲取環(huán)境變量的值,比如userName=${spring.application.name}
- 解析參數(shù)上的 @PathVariable 注解,如果 此注解上的 value()= id ,若 “{ id }” 不存在與 url , headers,queries中,那么將 “id” 加入MethodMetadata 的 formParams 屬性中,一般不容易出現(xiàn)這種情況
- 解析參數(shù)上的 @RequestHeader 注解,如果此參數(shù)類型是Map,設(shè)置下MethodMetadata 里的headerMapIndex,也就是參數(shù)序號(hào);如果不是,假設(shè)value()= uname,那么將uname 和 參數(shù)值 作為鍵值對(duì) 加入到MethodMetadata 的 template(RequestTemplate ) 的 headers 屬性中
- 解析參數(shù)上的 @RequestParam注解, 如果此參數(shù)類型是Map, 設(shè)置下MethodMetadata 里的queryMapIndex, 也就是參數(shù)序號(hào);如果不是,假設(shè)value()= uname,那么將uname 和 參數(shù)值 作為鍵值對(duì) 加入到MethodMetadata 的 template(RequestTemplate ) 的 queries屬性中
- @PathVariable,@RequestHeader, @RequestParam 這三個(gè)注解起作用的前提是SpringMVC里有他們的轉(zhuǎn)換器,能夠?qū)⑺麄冝D(zhuǎn)換為String。也就是說比如參數(shù)類型是Date,需要自定義一個(gè)Date > String 的轉(zhuǎn)換器,注入到ConversionService里;其他復(fù)雜類型也可以自定義相應(yīng)的轉(zhuǎn)換器。也就是這三個(gè)注解不是只能標(biāo)識(shí)基本數(shù)據(jù)類型,只要定義了相應(yīng)的轉(zhuǎn)化器,也可以標(biāo)識(shí)復(fù)雜類型。
- 一個(gè)參數(shù)可以標(biāo)識(shí)以上3種注解,不同的注解執(zhí)行時(shí)起不同的作用。不同的注解可能value()不同,也就是一個(gè)參數(shù)可能被放進(jìn)多個(gè)地方,比如 ( @PathVariable(“name”) @RequestHeader(“id”) @RequestParam (“flag”) String userName ) 。每一個(gè)注解都會(huì)將此參數(shù)順序和value() 存入 MethodMetadata 的 indexToName,以備后續(xù)執(zhí)行時(shí)解析。
- 只要參數(shù)標(biāo)識(shí)上述3個(gè)注解中的一個(gè),那么將參數(shù)序號(hào)和轉(zhuǎn)換器放入 MethodMetadata 的 indexToExpander 中;多種注解共用一個(gè)轉(zhuǎn)換器,類型是 ConvertingExpander,也就是要將參數(shù)轉(zhuǎn)化為Http請(qǐng)求中的數(shù)據(jù)。
- 如果參數(shù)上未標(biāo)識(shí)上述3種注解,那么此參數(shù)作為 RequestBody 的內(nèi)容。一個(gè)方法中只能有一個(gè)未標(biāo)識(shí)注解的參數(shù),將參數(shù)的序號(hào)和實(shí)際類型放入 MethodMetadata 的 bodyIndex 和 bodyType 中。
- 將MethodMetadata(接口Class和方法上的數(shù)據(jù)),@FeignClient注解里的數(shù)據(jù),Spring環(huán)境里配置的數(shù)據(jù)都放進(jìn) SynchronousMethodHandler 類型的對(duì)象中, 此對(duì)象將配合JDK的AOP動(dòng)態(tài)代理,代理對(duì)象執(zhí)行相應(yīng)方法時(shí)將其轉(zhuǎn)發(fā)給SynchronousMethodHandler 執(zhí)行,每一個(gè)Method對(duì)應(yīng)一個(gè)SynchronousMethodHandler
- 為@FeignClient標(biāo)識(shí)的接口創(chuàng)建JDK動(dòng)態(tài)代理對(duì)象,InvocationHandler類型為 :FeignInvocationHandler,持有Map< Method, MethodHandler > 類型的屬性,在調(diào)用相應(yīng)方法時(shí)轉(zhuǎn)發(fā)給指定的 MethodHandler 處理。
以上就是解析@FeignClient接口的,生成相應(yīng)接口的動(dòng)態(tài)代理對(duì)象的過程。
最終所有信息都匯總到SynchronousMethodHandler對(duì)象里,在實(shí)際執(zhí)行Http請(qǐng)求時(shí),根據(jù)接口上的參數(shù)數(shù)據(jù)和MethodHandler信息生成feign.Request對(duì)象,此對(duì)象里裝著當(dāng)前Http請(qǐng)求的所有信息,然后Feign將這些信息拷貝到RestTemplate中,就能執(zhí)行相應(yīng)的Http請(qǐng)求。
希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中如何使用?byte?數(shù)組作為?Map?的?key
本文將討論在使用HashMap時(shí),當(dāng)byte數(shù)組作為key時(shí)所遇到的問題及其解決方案,介紹使用String和List這兩種數(shù)據(jù)結(jié)構(gòu)作為臨時(shí)解決方案的方法,感興趣的朋友跟隨小編一起看看吧2023-06-06java實(shí)現(xiàn)簡單的圖書管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡單的圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07Myeclipse工程發(fā)布時(shí)端口占用問題的解決方法
這篇文章主要介紹了Myeclipse工程發(fā)布時(shí)端口占用問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Spring的@CrossOrigin注解使用與CrossFilter對(duì)象自定義詳解
這篇文章主要介紹了Spring的@CrossOrigin注解使用與CrossFilter對(duì)象自定義詳解,跨域,指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本,它是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制,所謂同源是指,域名,協(xié)議,端口均相同,需要的朋友可以參考下2023-12-12Spring中@Controller和@RestController的區(qū)別詳解
這篇文章主要介紹了Spring中@Controller和@RestController的區(qū)別詳解,@RestController?是?@Controller?和?@ResponseBody?的結(jié)合體,單獨(dú)使用?@RestController?的效果與?@Controller?和?@ResponseBody?二者同時(shí)使用的效果相同,需要的朋友可以參考下2023-10-10