日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Feign的构建过程及自定义扩展功能

發布時間:2023/12/9 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Feign的构建过程及自定义扩展功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

spring-cloud-openfeign-core-2.1.1.RELEASE.jarHystrixFeign 的詳細構建過程:

@EnableFeignClients -> FeignClientsRegistrar 掃描 @Feign注解的類 -> FeignClientFactoryBean通過Targeter生產FeignClient -> Targeter通過Feign.Builder構建Feign -> Feign.Builder

1. 準備工作(配置)

  • FeignAutoConfiguration自動配置類
  • @Configuration@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")protected static class HystrixFeignTargeterConfiguration {@Bean@ConditionalOnMissingBeanpublic Targeter feignTargeter() {return new HystrixTargeter();}}@Configuration@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")protected static class DefaultFeignTargeterConfiguration {@Bean@ConditionalOnMissingBeanpublic Targeter feignTargeter() {return new DefaultTargeter();}}
  • feign.hystrix.HystrixFeign類存在時,將 HystrixTargeter 注冊為 Targeter 類型的 bean
  • feign.hystrix.HystrixFeign類不存在時,使用 DefaultTargeter 。
  • 看起來似乎可以使用自定義的Targeter代替Hystrix或默認的,這樣就可以自定義各種功能了。實際上不行,因為 Targeter 是 package 訪問級別的。

  • FeignClientsConfiguration
  • @Configuration public class FeignClientsConfiguration {@Bean@ConditionalOnMissingBeanpublic Retryer feignRetryer() {return Retryer.NEVER_RETRY;}@Bean@Scope("prototype")@ConditionalOnMissingBeanpublic Feign.Builder feignBuilder(Retryer retryer) {return Feign.builder().retryer(retryer);}@Configuration@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })protected static class HystrixFeignConfiguration {@Bean@Scope("prototype")@ConditionalOnMissingBean@ConditionalOnProperty(name = "feign.hystrix.enabled")public Feign.Builder feignHystrixBuilder() {return HystrixFeign.builder();}} }

    重要:Feign 以及內部類 Feign.Builder 都是 public 訪問級別,可以注入自定義的bean。

    2.EnableFeignClients與FeignClientsRegistrar類

    將使用@FeignClient注解的類注冊成spring bean,并使用注解中的配置

  • 在@EnableFeignClients注解中導入FeignClientsRegistrar類
  • FeignClientsRegistrar類實現了ImportBeanDefinitionRegistrar類,會由spring框架執行實現方法 registerBeanDefinitions(AnnotationMetaData, BeanDefinitionRegistry)
  • FeignClientsRegistrar中的 registerBeanDefinitions方法調用兩個方法
  • registerDefaultConfiguration:注冊默認的配置
  • registerFeignClients:注冊Feign客戶端(重點
  • registerFeignClients:獲取 @EnableFeignClients注解中定義的配置掃描feign客戶端
  • registerFeignClients:通過registerFeignClient(BeanDefinitionRegistry, AnnotationMetadata, Map)方法注冊每一個feignClient,過程:先獲取 @FeignClient注解中定義的配置,將配置應用在spring bean 工廠 FeignClientFactoryBean, 通過工廠類 FeignClientFactoryBean 為每一個使用@FeignClient注解的類生產 FeignClient,詳細過程見下一節
  • 3.FeignClientFactoryBean

    FeignClient工廠bean。

    class FeignClientFactoryBeanimplements FactoryBean<Object>, InitializingBean, ApplicationContextAware{//... }

    通過實現方法 FactoryBean#getObject()來由spring框架生產FeignClient。

    @Override public Object getObject() throws Exception {return getTarget(); }/*** 獲得目標* 1. 獲得FeignContext* 2. 從FeignContext中獲得Feign構建器Feign.Builder* 3. 從FeignContext中獲得Client,判斷是否進行負載均衡* 4. 從FeignContext中獲得Target,并執行Target的默認方法target(FeignClientFactoryBean, Feign.Builder,FeignContext, Target.HardCodedTarget<T>);* 5.由于一開始注入的Feign.Builder是HystrixFeign.Builder,則此處是調用HystrixFeign.Builder里的對應方法*/ <T> T getTarget() {FeignContext context = this.applicationContext.getBean(FeignContext.class);Feign.Builder builder = feign(context);//省略部分代碼// ......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 unwrapclient = ((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)); }protected Feign.Builder feign(FeignContext context) {FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);Logger logger = loggerFactory.create(this.type);// @formatter:offFeign.Builder builder = get(context, Feign.Builder.class)// required values.logger(logger).encoder(get(context, Encoder.class)).decoder(get(context, Decoder.class)).contract(get(context, Contract.class));// @formatter:onconfigureFeign(context, builder);return builder;}

    工廠獲得對象(目標):

    1. 獲得FeignContext(feign上下文) 2. 從FeignContext中獲得Feign構建器Feign.Builder(public,可以在此使用自定義構建器) 3. 從FeignContext中獲得Client,判斷是否進行負載均衡 4. 從FeignContext中獲得Target,并執行Target的默認方法target(FeignClientFactoryBean, Feign.Builder,FeignContext, Target.HardCodedTarget<T>); 5. 由于一開始注入的 *Targeter* 是 *HystrixTargeter* ,則此處是調用 HystrixTargeter 里的對應方法(從第一節的配置來看,只要 *feign.hystrix.HystrixFeign* 類存在,就是注入的 *HystrixTargeter *, 否則是 *DefaultTargeter*,對于需要**自定義構建feign的,這里不太重要**)

    4.Targeter

    4.1.HystrixTargeter

    class HystrixTargeter implements Targeter {@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {// 若不是 HystrixFeign,則執行其對應的默認target方法。// 此處只處理HystrixFeign。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);}// 調用從Feign.Builder繼承的方法。return feign.target(target);}private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,Class<?> fallbackFactoryClass) {FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass,FallbackFactory.class);return builder.target(target, fallbackFactory);}private <T> T targetWithFallback(String feignClientName, FeignContext context,Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,Class<?> fallback) {T fallbackInstance = getFromContext("fallback", feignClientName, context,fallback, target.type());return builder.target(target, fallbackInstance);}//... }
  • HystrixTarget只處理 Feign.Builder 類型為 feign.hystrix.HystrixFeign.Builder 的
  • 若feign構建器不是 feign.hystrix.HystrixFeign.Builder 類型,則執行注入的 feign 構建器的默認target方法
  • 因此,即使注入的 Targeter 是 HystrixTargeter,此處也可以執行自定義 Feign.Builder。
  • 理解:Feign.Builder#target(Target) 方法通常不會被 override(后續會講解為什么不重寫此方法)
  • 4.2.DefaultTargeter

    class DefaultTargeter implements Targeter {@Overridepublic <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {return feign.target(target);} }
  • 執行 Feign.Builder (子)類型對應的 默認 target方法。
  • 理解:Feign.Builder#target(Target) 方法通常不會被 override(后續會講解為什么不重寫此方法)
  • 5.FeignBuilder

    feign構建器:構建feign對象。

    Feign的目的是將 http api 包裝成 restful 風格以便開發。

    在實現中,Feign 是一個為目標http apis 生成 feign對象(Feign#newInstance)的工廠。

    上述步驟目前需要的都是通過對應的 Builder 構建對應的Feign。

    public abstract class Feign {public static Builder builder() {return new Builder();}public abstract <T> T newInstance(Target<T> target);public static class 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);}} }
  • Feign.Builder#target(Target) 方法里面實際上調用的是 build() 方法來構建對象,因此重寫 build() 方法即可,沒有必要還重寫 target(Target) 方法
  • Feign 以及內部類 Feign.Builder 都是 public ,可以重寫并注入自定義的bean
  • 5.1.HystrixFeign

    public final class HystrixFeign {public static final class Builder extends Feign.Builder { @Overridepublic Feign build() {return build(null);}// 提供一系列的target方法,支持各種配置:fallback、FallBackFactory等public <T> T target(Target<T> target, T fallback) {return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null).newInstance(target);}public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {return build(fallbackFactory).newInstance(target);}public <T> T target(Class<T> apiType, String url, T fallback) {return target(new Target.HardCodedTarget<T>(apiType, url), fallback);}public <T> T target(Class<T> apiType,String url,FallbackFactory<? extends T> fallbackFactory) {return target(new Target.HardCodedTarget<T>(apiType, url), fallbackFactory);}/** Configures components needed for hystrix integration. */Feign build(final FallbackFactory<?> nullableFallbackFactory) {super.invocationHandlerFactory(new InvocationHandlerFactory() {@Overridepublic InvocationHandler create(Target target,Map<Method, MethodHandler> dispatch) {return new HystrixInvocationHandler(target, dispatch, setterFactory,nullableFallbackFactory);}});super.contract(new HystrixDelegatingContract(contract));return super.build();}

    基本到了這一步,需要設置的東西,都可以配置了。

  • 雖然 build 方法中涉及到 InvocationHandler,但基本不需要改什么,而 InvocationHandler 竟然也是 package 訪問級別,所以只好復制一個,使用自己的。
  • HystrixDelegatingContract 是 public 級別,不需要修改的話,仍然用這個。
  • 5.2示例

    以下示例參考 SentinelFeign

    @Override public Feign build() {super.invocationHandlerFactory(new InvocationHandlerFactory() {@Overridepublic InvocationHandler create(Target target,Map<Method, MethodHandler> dispatch) {// using reflect get fallback and fallbackFactory properties from// FeignClientFactoryBean because FeignClientFactoryBean is a package// level class, we can not use it in our packageObject feignClientFactoryBean = Builder.this.applicationContext.getBean("&" + target.type().getName());Class fallback = (Class) getFieldValue(feignClientFactoryBean,"fallback");Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,"fallbackFactory");String name = (String) getFieldValue(feignClientFactoryBean, "name");Object fallbackInstance;FallbackFactory fallbackFactoryInstance;// check fallback and fallbackFactory properties// 以下邏輯在HystrixTargeter中有,但執行自定義的builder,不會執行到那段邏輯,因此此處加上。if (void.class != fallback) {fallbackInstance = getFromContext(name, "fallback", fallback,target.type());return new PegasusInvocationHandler(target, dispatch, setterFactory,new FallbackFactory.Default(fallbackInstance));}if (void.class != fallbackFactory) {fallbackFactoryInstance = (FallbackFactory) getFromContext(name,"fallbackFactory", fallbackFactory,FallbackFactory.class);return new PegasusInvocationHandler(target, dispatch, setterFactory,fallbackFactoryInstance);}// 此處還是會使用一個默認的FallbackFactory。return new PegasusInvocationHandler(target, dispatch, setterFactory, new PegasusFeignFallbackFactory<>(target));}private Object getFromContext(String name, String type,Class fallbackType, Class targetType) {Object fallbackInstance = feignContext.getInstance(name,fallbackType);if (fallbackInstance == null) {throw new IllegalStateException(String.format("No %s instance of type %s found for feign client %s",type, fallbackType, name));}if (!targetType.isAssignableFrom(fallbackType)) {throw new IllegalStateException(String.format("Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",type, fallbackType, targetType, name));}return fallbackInstance;}});super.contract(new HystrixDelegatingContract(contract));return super.build(); }

    需要自定義fallbackFactory,則實現 feign.hystrix.FallbackFactory類,需要自定義fallback,則實現 org.springframework.cglib.proxy.MethodInterceptor即可

    6.總結

  • 由于Feign構建過程所用到的 Targeter 是 package 訪問級別的,不能使用自定義的
  • Feign以及Feign.Builder是 publilc,給了我們擴展的空間。
  • 7.參考資料

  • feign-hystrix-10.1.0.jarspring-cloud-openfeign-core-2.1.1.RELEASE.jar
  • spring-cloud-alibaba-sentinel-0.9.0.RELEASE.jar中的 sentinelFeign 實現
  • Spring Cloud Alibaba Sentinel 整合 Feign 的設計實現
  • 轉載于:https://blog.51cto.com/14355901/2399829

    總結

    以上是生活随笔為你收集整理的Feign的构建过程及自定义扩展功能的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。