美文网首页Spring Cloudspring cloud
Spring Cloud Feign 分析(五)之FeignCl

Spring Cloud Feign 分析(五)之FeignCl

作者: Blog | 来源:发表于2021-03-26 22:51 被阅读0次

    使用过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的调用过程,因为知道整个调用过程对我们排查问题非常有用!

    相关文章

      网友评论

        本文标题:Spring Cloud Feign 分析(五)之FeignCl

        本文链接:https://www.haomeiwen.com/subject/oqqfhltx.html