Spring Cloud Feign 分析(五)之FeignClient代理生成过程

使用过FeignClient的同学可能都知道,FeignClient在使用的过程中会生成一个代理类,所有操作都是由代理类去完成,这个的确是这样的,但是当有人问这个代理类是如何生成的?这个代理类做了哪些事情呢?不熟悉代理生成过程的我们可能就会不知如何回答,所以本节的我们使命就是分析FeignClient代理生成过程!


FeignClientFactoryBean

Spring Cloud Feign 分析(一)之FeignClient注册过程中讲解过FeignClientsRegistrar#registerFeignClients这个方法中return factoryBean.getObject();会返回一个代理对象,但是并没有深入讲解,特此放到本节进行统一讲解!

public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
        ApplicationContextAware, BeanFactoryAware {
    ......
    //创建HystrixFeign.builder
    protected Feign.Builder feign(FeignContext context) {
        //创建DefaultFeignLoggerFactory
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        //默认返回Slf4jLogger
        Logger logger = loggerFactory.create(type);
        //获取HystrixFeign.Builder
        Feign.Builder builder = get(context, Feign.Builder.class)
                 //默认Slf4jLogger
                .logger(logger)
                //默认SpringEncoder
                .encoder(get(context, Encoder.class))
                //默认SpringDecoder
                .decoder(get(context, Decoder.class))
                //默认SpringMvcContract
                .contract(get(context, Contract.class));
        //设置Feign.Builder 的其他配置,如ErrorDecoder、Retryer等配置
        configureFeign(context, builder);
        //扩展给外部使用者,外部使用者可以给Feign.Builder设置其他参数
        applyBuildCustomizers(context, builder);
        //返回Feign.Builder => HystrixFeign.Builder
        return builder;
    }
    //获取代理对象
    @Override
    public Object getObject() {
        return getTarget();
    }
    //代理对象实现过程,根据url参数区分使用哪一种策略
    <T> T getTarget() {
        //获取FeignContext工厂类
        FeignContext context = beanFactory != null
                ? beanFactory.getBean(FeignContext.class)
                : applicationContext.getBean(FeignContext.class);
        //获取HystrixFeign.builder()
        Feign.Builder builder = feign(context);
        //如果@FeignClient中为配置url,则使用如下方式,负载均衡方式
        if (!StringUtils.hasText(url)) {
            if (!name.startsWith("http")) {
                url = "http://" + name;
            }
            else {
                url = name;
            }
            url += cleanPath();
            //设置client为LoadBalancerFeignClient,具备负载均衡
            //返回HystrixTargeter.target中返回的代理类
            return (T) loadBalance(builder, context,
                    new HardCodedTarget<>(type, name, url));
        }
        //@FeignClient中配置的url,使用如下方式,以下方式均为不使用负载均衡的方式
        if (StringUtils.hasText(url) && !url.startsWith("http")) {
            url = "http://" + url;
        }
        String url = this.url + cleanPath();
        Client client = getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                client = ((LoadBalancerFeignClient) client).getDelegate();
            }
            if (client instanceof FeignBlockingLoadBalancerClient) {
                client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
            }
            //返回的client为Client.Default,不具备负载均衡作用
            builder.client(client);
        }
        //返回HystrixTargeter.target中返回的代理类
        Targeter targeter = get(context, Targeter.class);
        return (T) targeter.target(this, builder, context,
                new HardCodedTarget<>(type, name, url));
    }
}

经过我们不懈努力,FeignClientFactoryBean这个代理工厂类中代码片段已经标上了注释信息,为了便于我们更好的理解,我们汇总一下执行步骤:

  1. 获取FeignContext这个Feign工厂类上下文,用于从中获取所需的Bean
  2. 从FeignContext上下文中获取HystrixFeign.builder
  3. 设置HystrixFeign.builder的logger(Slf4jLogger)、encoder(默认SpringEncoder)、decoder(默认SpringDecoder)、contract(默认SpringMvcContract),如果有自定义实现则使用,否则使用默认值
  4. 设置Feign.Builder 的其他配置,如ErrorDecoder、Retryer等配置
  5. 将HystrixFeign.builder外抛给外部用于扩展,使用者可以设置相关参数
  6. 根据@FeignClient中配置是否配置了url参数,为Feign.Builder设置client参数,client分为负载均衡方式与非负载均衡方式
  7. 返回HystrixTargeter.target中返回的代理类

HystrixTargeter

class HystrixTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        //是否为Feign.Builder 类型,若不是则直接创建代理对象并执行
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            return feign.target(target);
        }
        //转换为HystrixFeign.Builder类型
        feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
        //获取上下文id,其实就是获取的@FeignClient注解的name、value属性值
        String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
                : factory.getContextId();
        //获取SetterFactory,主要是HystrixCommand的groupKey、commandKey参数,默认setterFactory为空
        SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
        //setterFactory不为空就设置
        if (setterFactory != null) {
            builder.setterFactory(setterFactory);
        }
        //获取降级方法,默认为void初始状态
        Class<?> fallback = factory.getFallback();
        if (fallback != void.class) {
            //如果有设置了fallback,则使用
            return targetWithFallback(name, context, target, builder, fallback);
        }
        //获取降级工厂类FallbackFactory,默认为void初始状态
        Class<?> fallbackFactory = factory.getFallbackFactory();
        if (fallbackFactory != void.class) {
            return targetWithFallbackFactory(name, context, target, builder,
                    fallbackFactory);
        }
        //调用HystrixFeign#build()
        return feign.target(target);
    }
    //具有FallbackFactory的目标执行类
    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);
        //返回具有FallbackFactory的代理实例
        return builder.target(target, fallbackFactory);
    }
    //具有Fallback的目标执行类
    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());
        //返回具有fallback的代理实例
        return builder.target(target, fallbackInstance);
    }
    //返回指定类型的实例
    private <T> T getFromContext(String fallbackMechanism, String feignClientName,
            FeignContext context, Class<?> beanType, Class<T> targetType) {
        ......
        return (T) fallbackInstance;
    }
    //根据@FeignClient注解的name、value属性值获取对应beanType实例
    private <T> T getOptional(String feignClientName, FeignContext context,
            Class<T> beanType) {
        return context.getInstance(feignClientName, beanType);
    }
}

通过上文中FeignClientFactoryBean的步骤中可知,在HystrixTargeter#target这个方法中最后一段return feign.target(target);经过的步骤如下:

  1. 调用Feign#Builder#target(Target<T> target)
  2. 调用HystrixFeign#build()
  3. 调用HystrixFeign#build(final FallbackFactory<?> nullableFallbackFactory)
  4. 调用Feign#build()
  5. 调用ReflectiveFeign#newInstance(Target<T> target)
  6. 返回代理对象proxy

HystrixTargeter汇总的步骤大约是6个步骤,但是这6个步骤因为存在来回调用,所以在下文里会重点分析每个步骤具体做了哪些事情,这样才有利于我们清楚生成代理对象的整个过程!


Feign#Builder

public abstract class Feign {
  ......
  //会调用到ReflectiveFeign#newInstance(Target<T> target)
  public abstract <T> T newInstance(Target<T> target);
  //返回代理对象
  public static class Builder {
    public <T> T target(Target<T> target) {
      //build()这个方法会调用到HystrixFeign#build()
      return build().newInstance(target);
    }
    //基础build,Capability可以理解为增强功能,通过增强,可以对Client这些配置进行额外操作,默认不会增强,返回原始值
    public Feign build() {
      //默认返回原始Client,以下都是未增强的,都是原始值
      Client client = Capability.enrich(this.client, capabilities);
      Retryer retryer = Capability.enrich(this.retryer, capabilities);
      List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
          .map(ri -> Capability.enrich(ri, capabilities))
          .collect(Collectors.toList());
      Logger logger = Capability.enrich(this.logger, capabilities);
      Contract contract = Capability.enrich(this.contract, capabilities);
      Options options = Capability.enrich(this.options, capabilities);
      Encoder encoder = Capability.enrich(this.encoder, capabilities);
      Decoder decoder = Capability.enrich(this.decoder, capabilities);
      //InvocationHandlerFactory工厂类,负责创建InvocationHandler代理类的执行方法
      InvocationHandlerFactory invocationHandlerFactory =
          Capability.enrich(this.invocationHandlerFactory, capabilities);
      QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
      //负责创建SynchronousMethodHandler方法执行Handler的工厂类
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
      //用于解析接口得到方法名称和对应的MethodHandler
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      //返回代理实例
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }
  ......
}

在调用target(Target<T> target)方法之后,因为HystrixFeign#Builder继承Feign.Builder,所以默认会执行下文中的HystrixFeign#build方法,然后HystrixFeign#build()方法中会设置Hystrix相关的配置,在Feign#build()这个方法中会通过增强Capability接口进行获取相关的配置,可以对Client这些配置进行额外操作,默认不会增强,返回原始值,最终将相关的参数设置给ReflectiveFeign并返回最终的代理对象!


HystrixFeign#build

public final class HystrixFeign {
  public static final class Builder extends Feign.Builder {
    ......
    @Override
    public Feign build() {
      return build(null);
    }
    //配置组件具备hystrix功能
    Feign build(final FallbackFactory<?> nullableFallbackFactory) {
      //设置代理对象的InvocationHandler,会将执行的方法HystrixCommand.execute(),这样就具备了Hystrix的熔断能力
      super.invocationHandlerFactory(new InvocationHandlerFactory() {
        @Override
        public InvocationHandler create(Target target,
                                        Map<Method, MethodHandler> dispatch) {
          //HystrixInvocationHandler实现了InvocationHandler,会在ReflectiveFeign#newInstance中设置到代理类中
          return new HystrixInvocationHandler(target, dispatch, setterFactory,
              nullableFallbackFactory);
        }
      });
      //将我们原本的contract协议具备Hystrix能力
      super.contract(new HystrixDelegatingContract(contract));
      //调用父类Feign#Builder#build()
      return super.build();
    }
    ......
  }
}

HystrixFeign#build这个方法就比较直观,就是配置组件具备hystrix功能,通过创建具备Hystrix熔断功能的InvocationHandler拦截器,然后内部通过HystrixCommand.execute()来触发请求,contract也包装给HystrixDelegatingContract让其具备Hystrix处理能力!


ReflectiveFeign#newInstance

public class ReflectiveFeign extends Feign {
  ......
  @Override
  public <T> T newInstance(Target<T> target) {
    //ParseHandlerByName解析接口得到方法名称和对应的MethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    //方法和对应的MethodHandler
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    //默认方法集合
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    //反射获取@FeignClient接口声明方法
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        //Default methods are public non-abstract, non-synthetic, and non-static instance methods
        //正常情况下@FeignClient接口不会走这里
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        //@FeignClient接口方法放入到methodToHandler中
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    //通过invocationHandlerFactory创建HystrixInvocationHandler
    InvocationHandler handler = factory.create(target, methodToHandler);
    //JDK动态代理创建代理对象并设置代理类的InvocationHandler,这样就可以通过Hystrix发起请求
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
    //将接口的默认方法绑定到生成的代理对象
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
  ......
}

ReflectiveFeign#newInstance这个方法中,我们可以看到其实就是在解析target这个对象,因为这个target是由@FeignClient注解包装而来的,所以大致步骤为:

  1. 通过ParseHandlerByName解析接口得到方法名称和对应的MethodHandler,其中Key => Class#Function(String,String,String),Class即为定义了@FeignClient注解的接口类名,Function则为该接口中的方法名
  2. 反射获取@FeignClient接口声明方法,将其加入methodToHandler这个方法和对应的MethodHandler
  3. 通过invocationHandlerFactory创建HystrixInvocationHandler
  4. JDK动态代理创建代理对象并设置代理类的InvocationHandler,这样就可以通过Hystrix发起请求
  5. 将接口的默认方法绑定到生成的代理对象 => 正常情况不会有
  6. 返回代理对象proxy

生成代理对象这个过程非常的复杂,我也只是总结了自己认为比较重要的部分,如果有兴趣可以在看看ParseHandlerByName这个解析接口的过程,相信对我们的代码设计能力有所帮助,后续的章节会继续讲解@FeignClient的调用过程,因为知道整个调用过程对我们排查问题非常有用!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容