Spring Cloud Feign原理詳解
Feign 主要是幫助我們方便進(jìn)行rest api服務(wù)間的調(diào)用,其大體實(shí)現(xiàn)思路就我們通過標(biāo)記注解在一個(gè)接口類上(注解上將包含要調(diào)用的接口信息),之后在調(diào)用時(shí)根據(jù)注解信息組裝好請(qǐng)求信息,接下來基于ribbon這些負(fù)載均衡器來生成真實(shí)的服務(wù)地址,最后將請(qǐng)求發(fā)送出去;之后將接收到的結(jié)果反序列化為相關(guān)的Java對(duì)象供我們直接使用。 下面我們走進(jìn)Spring Cloud對(duì)feign封裝的源碼中去了解其主要實(shí)現(xiàn)機(jī)制。
Feign的大體機(jī)制
通過在啟動(dòng)類上標(biāo)記 @EnableFeignClients 注解來開啟feign的功能,服務(wù)啟動(dòng)后會(huì)掃描 @FeignClient 注解標(biāo)記的接口,然后根據(jù)掃描的注解信息為每個(gè)接口類生成feign客戶端請(qǐng)求,同時(shí)解析接口方法中的Spring MVC的相關(guān)注解,通過專門的注解解析器識(shí)別這些注解信息,以便后面可以正確的組裝請(qǐng)求參數(shù),使用 Ribbon 和 Eureka 獲取到請(qǐng)求服務(wù)的真實(shí)地址等信息,最后使用 http 相關(guān)組件進(jìn)行執(zhí)行調(diào)用。其大致流程圖如下:

@EnableFeignClients 和 @FeignClient 注解
在EnableFeignClients 注解類中有一個(gè) @Import(FeignClientsRegistrar.class)的配置
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 引入FeignClientsRegistrar 來掃描@FeignClient注解下的類
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
...
}
我們追蹤代碼進(jìn)入到FeignClientsRegistrar類中,會(huì)發(fā)現(xiàn)FeignClientsRegistrar 類實(shí)現(xiàn)了ImportBeanDefinitionRegistrar(在spring context 項(xiàng)目中)接口,因此spring boot啟動(dòng)時(shí)會(huì)調(diào)用它的registerBeanDefinitions()方法,該方法中會(huì)掃描 EnableFeignClients 和 FeignClient 注解信息并設(shè)置相關(guān)信息。
/**
* spring boot 啟動(dòng)時(shí)會(huì)自動(dòng)調(diào)用 ImportBeanDefinitionRegistrar 入口方法
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata
, BeanDefinitionRegistry registry) {
// 讀取 @EnableFeignClients 注解中信息
registerDefaultConfiguration(metadata, registry);
// 掃描所有@FeignClient注解的類
registerFeignClients(metadata, registry);
}
registerDefaultConfiguration方法
在registerDefaultConfiguration()方法中會(huì)讀取@EnableFeignClients注解信息,然后將這些信息注冊(cè)到一個(gè) BeanDefinitionRegistry 里面去;之后feign的一些默認(rèn)配置將通過這里注冊(cè)的信息中取獲取。
registerFeignClients方法
- registerFeignClients()方法會(huì)掃描相關(guān)包路徑(如果EnableFeignClients的basePackages沒有配置,默認(rèn)會(huì)直接使用啟動(dòng)類所在的包路徑)下所有的@FeiginClient注解的類
- 然后根據(jù)@FeiginClient注解信息向BeanDefinitionRegistry里面注冊(cè)bean,注意這里設(shè)置的bean名稱生成規(guī)則是使用服務(wù)名+FeignClientSpecification.class.getSimpleName(),因此如果對(duì)一個(gè)服務(wù)寫多個(gè)接口類會(huì)發(fā)生bean名稱重復(fù)導(dǎo)致注冊(cè)失敗。所以需要增加一個(gè) allow-bean-definition-overriding: true 的配置。
- 最后會(huì)調(diào)用 registerFeignClient() 方法注冊(cè)feign客戶端,這里的bean名稱的為當(dāng)前接口類的類路徑。
其流程圖如下:

feign客戶端的動(dòng)態(tài)代理
上面registerFeignClient()方法中在構(gòu)建bean的時(shí)候,實(shí)際構(gòu)建的是FeignClientFactoryBean。
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
FeignClientFactoryBean 類對(duì)父類的getObject()方法進(jìn)行了重寫,后面動(dòng)態(tài)代理時(shí)使用的就是它來獲取feign client的。在這里會(huì)根據(jù)上面注解配置,同時(shí)會(huì)讀取application.yml配置信息,根據(jù)配置來設(shè)置feign的相關(guān)信息,比如編解碼器、注解解析器、請(qǐng)求超時(shí)時(shí)間等;之后如果沒有設(shè)置url那么就會(huì)和負(fù)載均衡器(ribbon)整合。最后會(huì)通過反射將接口中相關(guān)方法進(jìn)行解析保存供后面進(jìn)行jdk代理使用。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判斷是否是不需要代理的
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// 需要代理,執(zhí)行代理方法
return dispatch.get(method).invoke(args);
}

以上就是Spring Cloud Feign原理詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Cloud Feign原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
簡潔實(shí)用的Java Base64編碼加密異常處理類代碼
這篇文章主要介紹了簡潔實(shí)用的Java Base64編碼加密異常處理類代碼,有一定的實(shí)用價(jià)值,需要的朋友可以參考下2014-07-07
Mybatis關(guān)聯(lián)查詢遇到的坑-無主鍵的關(guān)聯(lián)數(shù)據(jù)去重問題
這篇文章主要介紹了Mybatis關(guān)聯(lián)查詢遇到的坑-無主鍵的關(guān)聯(lián)數(shù)據(jù)去重問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
Java編程實(shí)現(xiàn)生成給定范圍內(nèi)不重復(fù)隨機(jī)數(shù)的方法小結(jié)
這篇文章主要介紹了Java編程實(shí)現(xiàn)生成給定范圍內(nèi)不重復(fù)隨機(jī)數(shù)的方法,結(jié)合實(shí)例形式總結(jié)分析了java基于數(shù)學(xué)運(yùn)算與判斷實(shí)現(xiàn)不重復(fù)隨機(jī)數(shù)的生成功能,需要的朋友可以參考下2017-07-07
基于IDEA查看maven依賴結(jié)構(gòu)流程解析
這篇文章主要介紹了基于IDEA查看maven依賴結(jié)構(gòu)流程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
SpringBoot優(yōu)雅接收前端請(qǐng)求參數(shù)的詳細(xì)過程
這篇文章主要介紹了SpringBoot如何優(yōu)雅接收前端請(qǐng)求參數(shù),我們可以通過@RequestParm注解去綁定請(qǐng)求中的參數(shù),將(查詢參數(shù)或者form表單數(shù)據(jù))綁定到controller的方法參數(shù)中,本文結(jié)合示例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下2023-06-06
使用JPA主鍵@Id,@IdClass,@Embeddable,@EmbeddedId問題
這篇文章主要介紹了使用JPA主鍵@Id,@IdClass,@Embeddable,@EmbeddedId問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
通過實(shí)例深入學(xué)習(xí)Java的Struts框架中的OGNL表達(dá)式使用
這篇文章主要通過實(shí)例介紹了Java的Strus框架中的OGNL表達(dá)式使用,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11
詳解SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法
在本篇文章里面我們給大家詳細(xì)分析了SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法的相關(guān)知識(shí),對(duì)此有需要的朋友們學(xué)習(xí)下吧。2018-10-10

