Feign详细构建过程及自定义扩展
探究清楚 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
,详细过程见下一节
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
其中的 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,给了我们扩展的空间。
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 的设计实现