feign需要用到的注解(Feign详细构建过程及自定义扩展)
feign需要用到的注解(Feign详细构建过程及自定义扩展)1. 获得FeignContext(feign上下文)@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.
探究清楚 feign 的原理,自定义 feign 功能
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,详细过程见下一节
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.Targeter4.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(后续会讲解为什么不重写此方法)
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
其中的 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即可
6.总结- 由于Feign构建过程所用到的 Targeter 是 package 访问级别的,不能使用自定义的
- Feign以及Feign.Builder是 publilc,给了我们扩展的空间。
- 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 实现
- https://www.cnblogs.com/javastack/p/10129409.html