欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Feign详细构建过程及自定义扩展

程序员文章站 2022-07-01 16:32:21
探究清楚 feign 的原理,自定义 feign 功能 spring cloud openfeign core 2.1.1.RELEASE.jar 中 HystrixFeign 的详细构建过程: @EnableFeignClients FeignClientsRegistrar 扫描 @Feign注 ......

探究清楚 feign 的原理,自定义 feign 功能

spring-cloud-openfeign-core-2.1.1.release.jarhystrixfeign 的详细构建过程:

@enablefeignclients -> feignclientsregistrar 扫描 @feign注解的类 -> feignclientfactorybean通过targeter生产feignclient -> targeter通过feign.builder构建feign -> feign.builder

1. 准备工作(配置)

  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();
        }

    }
  1. feign.hystrix.hystrixfeign类存在时,将 hystrixtargeter 注册为 targeter 类型的 bean
  2. feign.hystrix.hystrixfeign类不存在时,使用 defaulttargeter
  3. 看起来似乎可以使用自定义的targeter代替hystrix或默认的,这样就可以自定义各种功能了。实际上不行,因为 targeterpackage 访问级别的。

  4. 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,并使用注解中的配置

  1. 在@enablefeignclients注解中导入feignclientsregistrar类
  2. feignclientsregistrar类实现了importbeandefinitionregistrar类,会由spring框架执行实现方法 registerbeandefinitions(annotationmetadata, beandefinitionregistry)
  3. feignclientsregistrar中的 registerbeandefinitions方法调用两个方法
    1. registerdefaultconfiguration:注册默认的配置
    2. registerfeignclients:注册feign客户端(重点
  4. registerfeignclients:获取 @enablefeignclients注解中定义的配置扫描feign客户端
  5. 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);
    }
    
    //...
}
  1. hystrixtarget只处理 feign.builder 类型为 feign.hystrix.hystrixfeign.builder
  2. 若feign构建器不是 feign.hystrix.hystrixfeign.builder 类型,则执行注入的 feign 构建器的默认target方法
  3. 因此,即使注入的 targeter 是 hystrixtargeter,此处也可以执行自定义 feign.builder
  4. 理解: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);
    }
}
  1. 执行 feign.builder (子)类型对应的 默认 target方法。
  2. 理解: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);
    }
  }
}
  1. feign.builder#target(target) 方法里面实际上调用的是 build() 方法来构建对象,因此重写 build() 方法即可,没有必要还重写 target(target) 方法
  2. 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();
    }

基本到了这一步,需要设置的东西,都可以配置了。

  1. 虽然 build 方法中涉及到 invocationhandler,但基本不需要改什么,而 invocationhandler 竟然也是 package 访问级别,所以只好复制一个,使用自己的。
  2. hystrixdelegatingcontract 是 public 级别,不需要修改的话,仍然用这个。

5.2示例

以下示例参考 sentinelfeign
其中的 yifeixiinvocationhandleryifeixifeignfallbackfactory是自定义的。

@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.总结

  1. 由于feign构建过程所用到的 targeterpackage 访问级别的,不能使用自定义的
  2. feign以及feign.builderpublilc,给了我们扩展的空间。

7.参考资料

  1. feign-hystrix-10.1.0.jarspring-cloud-openfeign-core-2.1.1.release.jar
  2. spring-cloud-alibaba-sentinel-0.9.0.release.jar中的 sentinelfeign 实现
  3. spring cloud alibaba sentinel 整合 feign 的设计实现