您好,登錄后才能下訂單哦!
小編給大家分享一下Feign如何構建以及實現自定義擴展功能,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的詳細構建過程:
@EnableFeignClients -> FeignClientsRegistrar 掃描 @Feign注解的類 -> FeignClientFactoryBean通過Targeter生產FeignClient -> Targeter通過Feign.Builder構建Feign -> Feign.Builder
FeignAutoConfiguration自動配置類
@Configuration @ConditionalOnClass(name = "feign.hystrix.HystrixFeign") protected static class HystrixFeignTargeterConfiguration { @Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new HystrixTargeter(); } } @Configuration @ConditionalOnMissingClass("feign.hystrix.HystrixFeign") protected static class DefaultFeignTargeterConfiguration { @Bean @ConditionalOnMissingBean public 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 @ConditionalOnMissingBean public Retryer feignRetryer() { return Retryer.NEVER_RETRY; } @Bean @Scope("prototype") @ConditionalOnMissingBean public 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。
將使用@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
,詳細過程見下一節
FeignClient工廠bean。
class FeignClientFactoryBean implements 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 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)); } protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); // @formatter:off Feign.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:on configureFeign(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的,這里不太重要**)
class HystrixTargeter implements Targeter { @Override public <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(后續會講解為什么不重寫此方法)
class DefaultTargeter implements Targeter { @Override public <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(后續會講解為什么不重寫此方法)
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。
public final class HystrixFeign { public static final class Builder extends Feign.Builder { @Override public 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() { @Override public 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 級別,不需要修改的話,仍然用這個。
以下示例參考 SentinelFeign
其中的 YiFeiXiInvocationHandler
和 YiFeiXiFeignFallbackFactory
是自定義的。
@Override public Feign build() { super.invocationHandlerFactory(new InvocationHandlerFactory() { @Override public 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 package Object 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 YiFeiXiInvocationHandler(target, dispatch, setterFactory, new FallbackFactory.Default(fallbackInstance)); } if (void.class != fallbackFactory) { fallbackFactoryInstance = (FallbackFactory) getFromContext(name, "fallbackFactory", fallbackFactory, FallbackFactory.class); return new YiFeiXiInvocationHandler(target, dispatch, setterFactory, fallbackFactoryInstance); } // 若注解中沒有使用fallback或fallbackFactory,則使用一個默認的FallbackFactory。 return new YiFeiXiInvocationHandler(target, dispatch, setterFactory, new YiFeiXiFeignFallbackFactory<>(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即可
以上是“Feign如何構建以及實現自定義擴展功能”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。