spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的詳細(xì)構(gòu)建過(guò)程:
@EnableFeignClients -> FeignClientsRegistrar 掃描 @Feign注解的類 -> FeignClientFactoryBean通過(guò)Targeter生產(chǎn)FeignClient -> Targeter通過(guò)Feign.Builder構(gòu)建Feign -> Feign.Builder
1. 準(zhǔn)備工作(配置)
- FeignAutoConfiguration自動(dòng)配置類
@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類存在時(shí)粟瞬,將
HystrixTargeter
注冊(cè)為Targeter
類型的 bean - feign.hystrix.HystrixFeign類不存在時(shí),使用
DefaultTargeter
儒飒。 - 看起來(lái)似乎可以使用自定義的Targeter代替Hystrix或默認(rèn)的,這樣就可以自定義各種功能了伦忠。實(shí)際上不行固以,因?yàn)?
Targeter
是 package 訪問(wèn)級(jí)別的滤蝠。
- 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 以及內(nèi)部類 Feign.Builder 都是 public 訪問(wèn)級(jí)別豌熄,可以注入自定義的bean。
2.EnableFeignClients與FeignClientsRegistrar類
將使用@FeignClient注解的類注冊(cè)成spring bean物咳,并使用注解中的配置
- 在@EnableFeignClients注解中導(dǎo)入FeignClientsRegistrar類
- FeignClientsRegistrar類實(shí)現(xiàn)了ImportBeanDefinitionRegistrar類锣险,會(huì)由spring框架執(zhí)行實(shí)現(xiàn)方法
registerBeanDefinitions(AnnotationMetaData, BeanDefinitionRegistry)
- FeignClientsRegistrar中的
registerBeanDefinitions
方法調(diào)用兩個(gè)方法-
registerDefaultConfiguration
:注冊(cè)默認(rèn)的配置 -
registerFeignClients
:注冊(cè)Feign客戶端(重點(diǎn))
-
-
registerFeignClients
:獲取@EnableFeignClients
注解中定義的配置掃描feign客戶端 -
registerFeignClients
:通過(guò)registerFeignClient(BeanDefinitionRegistry, AnnotationMetadata, Map)
方法注冊(cè)每一個(gè)feignClient,過(guò)程:先獲取@FeignClient
注解中定義的配置览闰,將配置應(yīng)用在spring bean 工廠FeignClientFactoryBean
芯肤, 通過(guò)工廠類FeignClientFactoryBean
為每一個(gè)使用@FeignClient
注解的類生產(chǎn)FeignClient
,詳細(xì)過(guò)程見(jiàn)下一節(jié)
3.FeignClientFactoryBean
FeignClient工廠bean压鉴。
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware{
//...
}
通過(guò)實(shí)現(xiàn)方法 FactoryBean#getObject()
來(lái)由spring框架生產(chǎn)FeignClient崖咨。
@Override
public Object getObject() throws Exception {
return getTarget();
}
/**
* 獲得目標(biāo)
* 1. 獲得FeignContext
* 2. 從FeignContext中獲得Feign構(gòu)建器Feign.Builder
* 3. 從FeignContext中獲得Client,判斷是否進(jìn)行負(fù)載均衡
* 4. 從FeignContext中獲得Target晴弃,并執(zhí)行Target的默認(rèn)方法target(FeignClientFactoryBean, Feign.Builder,
FeignContext, Target.HardCodedTarget<T>);
* 5.由于一開(kāi)始注入的Feign.Builder是HystrixFeign.Builder,則此處是調(diào)用HystrixFeign.Builder里的對(duì)應(yīng)方法
*/
<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;
}
工廠獲得對(duì)象(目標(biāo)):
1. 獲得FeignContext(feign上下文)
2. 從FeignContext中獲得Feign構(gòu)建器Feign.Builder(public,可以在此使用自定義構(gòu)建器)
3. 從FeignContext中獲得Client,判斷是否進(jìn)行負(fù)載均衡
4. 從FeignContext中獲得Target上鞠,并執(zhí)行Target的默認(rèn)方法target(FeignClientFactoryBean, Feign.Builder,
FeignContext, Target.HardCodedTarget<T>);
5. 由于一開(kāi)始注入的 *Targeter* 是 *HystrixTargeter* ,則此處是調(diào)用 HystrixTargeter 里的對(duì)應(yīng)方法(從第一節(jié)的配置來(lái)看际邻,只要 *feign.hystrix.HystrixFeign* 類存在,就是注入的 *HystrixTargeter *芍阎, 否則是 *DefaultTargeter*世曾,對(duì)于需要**自定義構(gòu)建feign的,這里不太重要**)
4.Targeter
4.1.HystrixTargeter
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 若不是 HystrixFeign谴咸,則執(zhí)行其對(duì)應(yīng)的默認(rèn)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);
}
// 調(diào)用從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構(gòu)建器不是 feign.hystrix.HystrixFeign.Builder 類型血巍,則執(zhí)行注入的 feign 構(gòu)建器的默認(rèn)target方法
- 因此,即使注入的 Targeter 是 HystrixTargeter,此處也可以執(zhí)行自定義 Feign.Builder珊随。
- 理解:Feign.Builder#target(Target) 方法通常不會(huì)被 override(后續(xù)會(huì)講解為什么不重寫(xiě)此方法)
4.2.DefaultTargeter
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
- 執(zhí)行 Feign.Builder (子)類型對(duì)應(yīng)的 默認(rèn) target方法述寡。
- 理解:Feign.Builder#target(Target) 方法通常不會(huì)被 override(后續(xù)會(huì)講解為什么不重寫(xiě)此方法)
5.FeignBuilder
feign構(gòu)建器:構(gòu)建feign對(duì)象。
Feign的目的是將 http api 包裝成 restful 風(fēng)格以便開(kāi)發(fā)叶洞。
在實(shí)現(xiàn)中鲫凶,F(xiàn)eign 是一個(gè)為目標(biāo)http apis 生成 feign對(duì)象(
Feign#newInstance
)的工廠。
上述步驟目前需要的都是通過(guò)對(duì)應(yīng)的 Builder 構(gòu)建對(duì)應(yīng)的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) 方法里面實(shí)際上調(diào)用的是 build() 方法來(lái)構(gòu)建對(duì)象螟炫,因此重寫(xiě) build() 方法即可,沒(méi)有必要還重寫(xiě) target(Target) 方法
- Feign 以及內(nèi)部類 Feign.Builder 都是 public 艺晴,可以重寫(xiě)并注入自定義的bean昼钻。
5.1.HystrixFeign
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();
}
基本到了這一步换吧,需要設(shè)置的東西,都可以配置了钥星。
- 雖然 build 方法中涉及到 InvocationHandler,但基本不需要改什么沾瓦,而 InvocationHandler 竟然也是 package 訪問(wèn)級(jí)別,所以只好復(fù)制一個(gè)谦炒,使用自己的贯莺。
- HystrixDelegatingContract 是 public 級(jí)別,不需要修改的話宁改,仍然用這個(gè)缕探。
5.2示例
以下示例參考 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中有还蹲,但執(zhí)行自定義的builder爹耗,不會(huì)執(zhí)行到那段邏輯耙考,因此此處加上。
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);
}
// 若注解中沒(méi)有使用fallback或fallbackFactory潭兽,則使用一個(gè)默認(rèn)的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,則實(shí)現(xiàn) feign.hystrix.FallbackFactory類山卦,需要自定義fallback鞋邑,則實(shí)現(xiàn) org.springframework.cglib.proxy.MethodInterceptor即可
6.總結(jié)
- 由于Feign構(gòu)建過(guò)程所用到的 Targeter 是 package 訪問(wèn)級(jí)別的,不能使用自定義的
- Feign以及Feign.Builder是 publilc账蓉,給了我們擴(kuò)展的空間枚碗。
7.參考資料
- feign-hystrix-10.1.0.jar和spring-cloud-openfeign-core-2.1.1.RELEASE.jar
- spring-cloud-alibaba-sentinel-0.9.0.RELEASE.jar中的 sentinelFeign 實(shí)現(xiàn)
- Spring Cloud Alibaba Sentinel 整合 Feign 的設(shè)計(jì)實(shí)現(xiàn)