SpringCloud全面解析@FeignClient標識接口的過程
Feign的作用
是將Http請求抽象化為一個Interface客戶端,可以調(diào)用接口的形式來執(zhí)行Http請求,以達到簡化Http調(diào)用的目的。
Feign將分散在@FeignClient,@EnableFeignClients,標識接口,接口方法,Spring環(huán)境上的各種配置信息提取出來封裝成一個對象,然后將對象里的信息注入到RestTemplate中,生成一次Http請求,然后執(zhí)行。
正常在SpringMVC的Controller
是將Http請求的信息提取出來注入@RequestMapping標識的方法中;而Feign是將接口中的信息提取出來,封裝成一個Http請求的相關(guān)信息,是對SpringMVC解析過程的一個逆向處理。
當我們通過IOC注入接口對象時,得到的肯定是此接口的實現(xiàn)類對象,這個對象應(yīng)該就是SpringCloud通過動態(tài)代理生成的對象。對于接口對象生成動態(tài)代理對象,一般選用JDK的Proxy,這樣實現(xiàn)簡單且耦合性低,SpringCloud就是如此。
SpringCloud將@FeignClient標識的接口
注冊成一個 FeignClientFactoryBean 類型的Bean對象,我們通過IOC注入的是此Bean的 getObject( ) 得到的對象,這篇文章主要就是講解此方法的執(zhí)行過程。
當然直接貼源碼那太無腦了,我主要是會將解析的過程總結(jié)成一個個點
讓大家明白在使用過程中需要注意以及可以靈活拓展的地方
- 在解析接口前,先加載SpringCloud的Feign配置,默認情況先加載 @FeignClient,@EnableFeignClients注解上的配置,其次加載 Spring環(huán)境里 feign.client.default 指定的配置,最后加載 feign.client.appName(應(yīng)用名稱) 指定的配置后續(xù)的配置信息會覆蓋之前的,也就是越靠后的優(yōu)先級越高??梢酝ㄟ^ “feign.client.defaultToProperties” 屬性來改變這種優(yōu)先級順序
- 驗證:接口方法參數(shù)長度不能為0
- 驗證:標識@FeignClient的接口最多只能繼承一個接口
- 驗證:@FeignClient標識的接口的父接口不能再繼承自其它接口,也就是@FeignClient的接口最多也只能有一個上級接口
- 解析接口方法時, 忽略這些類型的方法:Object的方法,Static方法,Default方法
- 將@FeignClient標識接口的最上級 Interface 的@RequestMapping注解的 value()值 設(shè)置為 MethodMetadata里的RequestTemplate的 “url” 的第一位;也就是說如果@FeignClient標識接口 有Super Interface,那么取Super Interface 的@RequestMapping;如果沒有,那么取自己的@RequestMapping;如果都沒有此注解,那么忽略
- 如果 Method 上未標識 @RequestMapping,忽略
- Method上的@RequestMapping ,其 method()和value()的值都最多只能有一個
- 將Method上 @RequestMapping 的 value() 追加到 RequestTemplate的 “url” 上,接在 接口上的 @RequestMapping 的value() 之后,機制匹配Controller。
- 解析Method上 @RequestMapping 上的 produces(),驗證其值只能有一個元素,將其值添加到Header上的 Accept 中,比如 Accept=application/json
- 解析 Method上@RequestMapping 上的 consumes(),驗證其值只能有一個元素,將其值添加到Header上的 Content-Type中,比如 Content-Type=application/json
- 解析 Method上@RequestMapping 上的 headers(),headers是一個String[],其元素是一個個的鍵值對,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ù)序號;如果不是,假設(shè)value()= uname,那么將uname 和 參數(shù)值 作為鍵值對 加入到MethodMetadata 的 template(RequestTemplate ) 的 headers 屬性中
- 解析參數(shù)上的 @RequestParam注解, 如果此參數(shù)類型是Map, 設(shè)置下MethodMetadata 里的queryMapIndex, 也就是參數(shù)序號;如果不是,假設(shè)value()= uname,那么將uname 和 參數(shù)值 作為鍵值對 加入到MethodMetadata 的 template(RequestTemplate ) 的 queries屬性中
- @PathVariable,@RequestHeader, @RequestParam 這三個注解起作用的前提是SpringMVC里有他們的轉(zhuǎn)換器,能夠?qū)⑺麄冝D(zhuǎn)換為String。也就是說比如參數(shù)類型是Date,需要自定義一個Date > String 的轉(zhuǎn)換器,注入到ConversionService里;其他復雜類型也可以自定義相應(yīng)的轉(zhuǎn)換器。也就是這三個注解不是只能標識基本數(shù)據(jù)類型,只要定義了相應(yīng)的轉(zhuǎn)化器,也可以標識復雜類型。
- 一個參數(shù)可以標識以上3種注解,不同的注解執(zhí)行時起不同的作用。不同的注解可能value()不同,也就是一個參數(shù)可能被放進多個地方,比如 ( @PathVariable(“name”) @RequestHeader(“id”) @RequestParam (“flag”) String userName ) 。每一個注解都會將此參數(shù)順序和value() 存入 MethodMetadata 的 indexToName,以備后續(xù)執(zhí)行時解析。
- 只要參數(shù)標識上述3個注解中的一個,那么將參數(shù)序號和轉(zhuǎn)換器放入 MethodMetadata 的 indexToExpander 中;多種注解共用一個轉(zhuǎn)換器,類型是 ConvertingExpander,也就是要將參數(shù)轉(zhuǎn)化為Http請求中的數(shù)據(jù)。
- 如果參數(shù)上未標識上述3種注解,那么此參數(shù)作為 RequestBody 的內(nèi)容。一個方法中只能有一個未標識注解的參數(shù),將參數(shù)的序號和實際類型放入 MethodMetadata 的 bodyIndex 和 bodyType 中。
- 將MethodMetadata(接口Class和方法上的數(shù)據(jù)),@FeignClient注解里的數(shù)據(jù),Spring環(huán)境里配置的數(shù)據(jù)都放進 SynchronousMethodHandler 類型的對象中, 此對象將配合JDK的AOP動態(tài)代理,代理對象執(zhí)行相應(yīng)方法時將其轉(zhuǎn)發(fā)給SynchronousMethodHandler 執(zhí)行,每一個Method對應(yīng)一個SynchronousMethodHandler
- 為@FeignClient標識的接口創(chuàng)建JDK動態(tài)代理對象,InvocationHandler類型為 :FeignInvocationHandler,持有Map< Method, MethodHandler > 類型的屬性,在調(diào)用相應(yīng)方法時轉(zhuǎn)發(fā)給指定的 MethodHandler 處理。
以上就是解析@FeignClient接口的,生成相應(yīng)接口的動態(tài)代理對象的過程。
最終所有信息都匯總到SynchronousMethodHandler對象里,在實際執(zhí)行Http請求時,根據(jù)接口上的參數(shù)數(shù)據(jù)和MethodHandler信息生成feign.Request對象,此對象里裝著當前Http請求的所有信息,然后Feign將這些信息拷貝到RestTemplate中,就能執(zhí)行相應(yīng)的Http請求。
希望能給大家一個參考,也希望大家多多支持腳本之家。
- SpringCloud中的@FeignClient注解使用詳解
- springcloud之FeignClient使用詳解
- SpringCloud @FeignClient注入Spring容器原理分析
- SpringCloud之@FeignClient()注解的使用詳解
- SpringCloud FeignClient 超時設(shè)置
- SpringCloud引入feign失敗或找不到@EnableFeignClients注解問題
- SpringCloud @FeignClient參數(shù)的用法解析
- SpringCloud之@FeignClient()注解的使用方式
- SpringCloud中FeignClient自定義配置
相關(guān)文章
Spring Data JPA使用JPQL與原生SQL進行查詢的操作
這篇文章主要介紹了Spring Data JPA使用JPQL與原生SQL進行查詢的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
spring使用WebSocket注入service層失敗問題及解決
這篇文章主要介紹了spring使用WebSocket注入service層失敗問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
Mybatis的@select和@SelectProvider注解方式動態(tài)SQL語句解讀
這篇文章主要介紹了Mybatis的@select和@SelectProvider注解方式動態(tài)SQL語句,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12

