共计 11108 个字符,预计需要花费 28 分钟才能阅读完成。
spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的详细构建过程:
@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
@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。
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 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 的,这里不太重要 **)
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,则执行其对应的默认 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 {
@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(后续会讲解为什么不重写此方法)
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 {
@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 级别,不需要修改的话,仍然用这个。
5.2 示例
以下示例参考 SentinelFeign
@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 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.jar和spring-cloud-openfeign-core-2.1.1.RELEASE.jar
- spring-cloud-alibaba-sentinel-0.9.0.RELEASE.jar中的 sentinelFeign 实现
- Spring Cloud Alibaba Sentinel 整合 Feign 的设计实现