最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于feign接口動態(tài)代理源碼解析

 更新時間:2022年03月09日 08:55:50   作者:keygod1  
這篇文章主要介紹了關(guān)于feign接口動態(tài)代理源碼解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

feign接口動態(tài)代理源碼解析

@FeignClinet 代理類注冊

@FeignClinet 通過動態(tài)代理實現(xiàn)的底層http調(diào)用,既然是動態(tài)代理,必然存在創(chuàng)建代理類的過程。如Proxy.newProxyInstance或者 CGlib org.springframework.cloud.openfeign 的代理類注冊實現(xiàn)如下。

首先,org.springframework.cloud.openfeign.FeignClientsRegistrar 注冊FeignClientFactoryBean到Singleton緩存中. 一個接口對應(yīng)FeignClientFactoryBean。

spring 初始化容器過程中執(zhí)行

org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject()
@Override
? ?public Object getObject() throws Exception {
? ??? ?return getTarget();
? ?}
? ?/**
? ? * @param <T> the target type of the Feign client
? ? * @return a {@link Feign} client created with the specified data and the context information
? ? */
? ?<T> T getTarget() {
? ??? ?FeignContext context = applicationContext.getBean(FeignContext.class);
? ??? ?Feign.Builder builder = feign(context);
? ??? ?if (!StringUtils.hasText(this.url)) {
? ??? ??? ?if (!this.name.startsWith("http")) {
? ??? ??? ??? ?url = "http://" + this.name;
? ??? ??? ?}
? ??? ??? ?else {
? ??? ??? ??? ?url = this.name;
? ??? ??? ?}
? ??? ??? ?url += cleanPath();
? ??? ??? ?return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
? ??? ??? ??? ??? ?this.name, url));
? ??? ?}
? ??? ?if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
? ??? ??? ?this.url = "http://" + this.url;
? ??? ?}
? ??? ?String url = this.url + cleanPath();
? ??? ?Client client = getOptional(context, Client.class);
? ??? ?if (client != null) {
? ??? ??? ?if (client instanceof LoadBalancerFeignClient) {
? ??? ??? ??? ?// not load balancing because we have a url,
? ??? ??? ??? ?// but ribbon is on the classpath, so unwrap
? ??? ??? ??? ?client = ((LoadBalancerFeignClient)client).getDelegate();
? ??? ??? ?}
? ??? ??? ?builder.client(client);
? ??? ?}
? ??? ?Targeter targeter = get(context, Targeter.class);
? ??? ?return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
? ??? ??? ??? ?this.type, this.name, url));
? ?}

其中 getObject() 實現(xiàn)了 FactoryBean 的 getObject(),

作用是在springContext初始化時創(chuàng)建Bean實例,如果isSingleton()返回true,則該實例會放到Spring容器的單實例緩存池中。

然后是targeter.target() 如果啟用了Hystrix調(diào)用的就是

org.springframework.cloud.openfeign.HystrixTargeter.target()

org.springframework.cloud.openfeign.HystrixTargeter

/**
* @param factory bean工廠
* @param feign ?feign對象的構(gòu)造類
* @param context feign接口上下文,
* @param target 保存了feign接口的name,url和FeignClient的Class對象
*
**/
@Override
? ?public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
? ??? ??? ??? ??? ??? ?Target.HardCodedTarget<T> target) {
? ??? ?if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
? ??? ??? ?return feign.target(target);
? ??? ?}
? ??? ?feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
? ??? ?SetterFactory setterFactory = getOptional(factory.getName(), context,
? ??? ??? ?SetterFactory.class);
? ??? ?if (setterFactory != null) {
? ??? ??? ?builder.setterFactory(setterFactory);
? ??? ?}
? ??? ?Class<?> fallback = factory.getFallback();
? ??? ?if (fallback != void.class) {
? ??? ??? ?return targetWithFallback(factory.getName(), context, target, builder, fallback);
? ??? ?}
? ??? ?Class<?> fallbackFactory = factory.getFallbackFactory();
? ??? ?if (fallbackFactory != void.class) {
? ??? ??? ?return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
? ??? ?}
? ??? ?return feign.target(target);
? ?}

再看下去 feign.target(target)

feign.Feign.Builder

? ? public <T> T target(Target<T> target) {
? ? ? return build().newInstance(target);
? ? }
? ? public Feign build() {
? ? ? SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
? ? ? ? ? new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
? ? ? ? ? ? ? logLevel, decode404, closeAfterDecode, propagationPolicy);
? ? ? ParseHandlersByName handlersByName =
? ? ? ? ? new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
? ? ? ? ? ? ? errorDecoder, synchronousMethodHandlerFactory);
? ? ? return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
? ? }

build() 返回一個ReflectiveFeign對象。

往下看,ReflectiveFeign的newInstance方法。

feign.ReflectiveFeign

@Override
? public <T> T newInstance(Target<T> target) {
? ? //關(guān)鍵方法: 解析target對象,返回key 為 feign接口的url ,value 為請求執(zhí)行類:SynchronousMethodHandler
? ? Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
? ? Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
? ? List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
? ? for (Method method : target.type().getMethods()) {
? ? ? if (method.getDeclaringClass() == Object.class) {
? ? ? ? continue;
? ? ? } else if (Util.isDefault(method)) {
? ? ? ? DefaultMethodHandler handler = new DefaultMethodHandler(method);
? ? ? ? defaultMethodHandlers.add(handler);
? ? ? ? methodToHandler.put(method, handler);
? ? ? } else {
? ? ? ? methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
? ? ? }
? ? }
? ? //創(chuàng)建代理類 handler ,返回對象 ?feign.ReflectiveFeign.FeignInvocationHandler
? ? InvocationHandler handler = factory.create(target, methodToHandler);
? ? T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
? ? ? ? new Class<?>[] {target.type()}, handler);
? ? for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
? ? ? defaultMethodHandler.bindTo(proxy);
? ? }
? ? return proxy;
? }

至此,代理類注冊完成。

當調(diào)用feign接口時,其實執(zhí)行的是 feign.ReflectiveFeign.FeignInvocationHandler的invoke 方法

@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();
? ? ? }
? ? ? ? //dispatch.get(method)返回的是 SynchronousMethodHandler 對象
? ? ? return dispatch.get(method).invoke(args);
? ? }

調(diào)用的 SynchronousMethodHandler invoke 方法。

feign.SynchronousMethodHandler

?@Override
? public Object invoke(Object[] argv) throws Throwable {
? ? RequestTemplate template = buildTemplateFromArgs.create(argv);
? ? Retryer retryer = this.retryer.clone();
? ? while (true) {
? ? ? try {
? ? ? ? return executeAndDecode(template);
? ? ? } catch (RetryableException e) {
? ? ? ? try {
? ? ? ? ? retryer.continueOrPropagate(e);
? ? ? ? } catch (RetryableException th) {
? ? ? ? ? Throwable cause = th.getCause();
? ? ? ? ? if (propagationPolicy == UNWRAP && cause != null) {
? ? ? ? ? ? throw cause;
? ? ? ? ? } else {
? ? ? ? ? ? throw th;
? ? ? ? ? }
? ? ? ? }
? ? ? ? if (logLevel != Logger.Level.NONE) {
? ? ? ? ? logger.logRetry(metadata.configKey(), logLevel);
? ? ? ? }
? ? ? ? continue;
? ? ? }
? ? }
? }

executeAndDecode 方法執(zhí)行RPC調(diào)用的邏輯。

小結(jié)一下:FeignClientsRegistrar 解析@FeignClient注解,注冊對應(yīng)的FeignClientFactoryBean–》通過FeignClientFactoryBean的getObject()方法返回代理對象 feign.ReflectiveFeign.FeignInvocationHandler

feign源碼解析

首先我要說的是springcloud沒有rpc,這就涉及rpc和微服務(wù)的區(qū)別。springcloud的模塊通信工具feign跟httpclient和okhttp是一樣的東西,都是對http請求封裝的工具,其實feign可以選擇httpclient或者okhttp作為底層實現(xiàn)(修改配置即可)。

Feign的作用

①封裝http請求,使開發(fā)人員對發(fā)送請求的過程無感知,給人一種偽rpc感覺(這也許是feign這個名字的由來吧,偽裝~)。

②feign整合ribbon和hystrix,結(jié)合eureka起到負載均衡和熔斷器、降級作用。

源碼及流程介紹

我們從@EnableFeignClients這個注解開始追蹤

我們發(fā)現(xiàn)有個@Import注解,引用FeignClientRegistrar類,跟進去看看

2個方法:①redisterDefalterConfiguration是加載配置,②registerFeignClients掃描你填寫的basepackage下的所有@FeignClient注解的接口。第一個方法沒啥好說的,我們主要看看第二個方法。

掃描完之后,把所有包含@FeignClient注解的接口都注冊到spring的beanfactory去,讓開發(fā)人員可以@Autowired來調(diào)用。這一部分代碼我就不貼了,我們只是追求feign的原理流程,太涉及spring源碼部分,我不做解釋。

=========== 以上是feign注冊流程,下面介紹拼裝request請求部分 ===========

首先,這里看ReflectiveFeign類,這個類用的是jdk的動態(tài)代理

用到代理模式肯定是在發(fā)送feign請求之前做一些操作,繼續(xù)看看請求之前做了哪些操作。

代理攔截每一個FeignClient請求,進入SynchronousMethodHandler的invoke方法,該方法調(diào)用executeAndDecode方法,這個方法看名字就知道是創(chuàng)建請求的方法,進去看看。

在該方法發(fā)送請求并且解碼,解碼分為decoder和errordecoder,這兩個都是可以重寫。這里你可能會問解碼器,那編碼器呢,feign默認用springEncoder,同樣是可以替換成Gson等。

=========== 以上是feign的調(diào)用流程,以下是feign使用過程的坑 ===========

①feign在D版本后默認關(guān)閉hystrix,要想傳遞請求頭,如果不用hystrix的話在feign攔截器里塞一遍就好;如果要用hystrix,那么改用信號量。

②在C版本后默認關(guān)閉hystrix,要使用要手動開啟

③不要妄想改變feign的邏輯,因為代理模式被寫成final,無法修改

④無法在解碼器里拋自定義異常,因為feign最終會統(tǒng)一攔截,拋出一個feignexception。你想把統(tǒng)一攔截也改了,那么你可以看看第③坑。

⑤feign的重試機制,默認是1,也就是說超時時間會變成2倍。這個可以通過配置修改。

⑥feign集成的負載均衡器ribbon,feign有個緩存,ribbon也有個緩存,會造成上線延遲,可以修改配置實現(xiàn)。

⑦feign對格式化時間處理有問題

⑧如果你是使用生產(chǎn)者提供api,并且實現(xiàn)該接口,@requestparam可以不用在實現(xiàn)類寫,但是@requestbody不寫無法映射

以上的坑都是我在實際工作中一個一個爬過來的,僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Mybatis配置之<properties>屬性配置元素解析

    Mybatis配置之<properties>屬性配置元素解析

    這篇文章主要介紹了Mybatis配置之<properties>屬性配置元素解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java堆&優(yōu)先級隊列示例講解(下)

    Java堆&優(yōu)先級隊列示例講解(下)

    這篇文章主要通過示例詳細為大家介紹Java中的堆以及優(yōu)先級隊列,文中的示例代碼講解詳細,對我們了解java有一定幫助,需要的可以參考一下
    2022-03-03
  • SpringBoot實現(xiàn)物品點贊功能

    SpringBoot實現(xiàn)物品點贊功能

    這篇文章主要介紹了SpringBoot物品點贊功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • Java文件上傳下載、郵件收發(fā)實例代碼

    Java文件上傳下載、郵件收發(fā)實例代碼

    這篇文章主要介紹了Java文件上傳下載、郵件收發(fā)實例代碼的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • 微信支付java版本之JSAPI支付+發(fā)送模板消息

    微信支付java版本之JSAPI支付+發(fā)送模板消息

    這篇文章主要介紹了微信支付java版本之JSAPI支付,發(fā)送模板消息,感興趣的小伙伴們可以參考一下
    2016-08-08
  • JVM生產(chǎn)環(huán)境調(diào)優(yōu)實戰(zhàn)案例講解

    JVM生產(chǎn)環(huán)境調(diào)優(yōu)實戰(zhàn)案例講解

    這篇文章主要介紹了JVM生產(chǎn)環(huán)境調(diào)優(yōu)實戰(zhàn),本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2025-04-04
  • SpringBoot Mybatis批量插入Oracle數(shù)據(jù)庫數(shù)據(jù)

    SpringBoot Mybatis批量插入Oracle數(shù)據(jù)庫數(shù)據(jù)

    這篇文章主要介紹了SpringBoot Mybatis批量插入Oracle數(shù)據(jù)庫數(shù)據(jù),文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08
  • JUnit測試控制@Test執(zhí)行順序的三種方式小結(jié)

    JUnit測試控制@Test執(zhí)行順序的三種方式小結(jié)

    這篇文章主要介紹了JUnit測試控制@Test執(zhí)行順序的三種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 使用Java實現(xiàn)生命游戲串行代碼示例

    使用Java實現(xiàn)生命游戲串行代碼示例

    生命游戲是一種二維細胞自動機,由英國數(shù)學(xué)家在1970年發(fā)明,在游戲的過程中,細胞會形成各種有規(guī)律的結(jié)構(gòu),展現(xiàn)出生命的復(fù)雜性和多樣性,本文通過java和JavaFX實現(xiàn)了一個簡單的生命游戲,可以直觀的觀察到細胞的迭代過程,需要的朋友可以參考下
    2024-10-10
  • Spring mvc結(jié)果跳轉(zhuǎn)方法詳解

    Spring mvc結(jié)果跳轉(zhuǎn)方法詳解

    這篇文章主要介紹了Spring mvc結(jié)果跳轉(zhuǎn)方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03

最新評論

五常市| 万山特区| 北海市| 巴彦县| 盖州市| 射阳县| 中方县| 天长市| 通河县| 清徐县| 清原| 运城市| 西安市| 板桥市| 察隅县| 若羌县| 铜川市| 寻乌县| 锡林郭勒盟| 陆川县| 西盟| 建始县| 孟连| 寻乌县| 新龙县| 广饶县| 布尔津县| 澜沧| 鹿邑县| 会泽县| 绍兴市| 淳化县| 桓仁| 阿巴嘎旗| 内乡县| 惠水县| 鄂尔多斯市| 惠州市| 盐边县| 彭山县| 涪陵区|