spring-cloud-openFeign源码深度解析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取 2000+ 道 Java 面试题

如何使用spring cloud feign

    @SpringBootApplication
    @EnableFeignClients
    public class WebApplication {

        public static void main(String[] args) {
            SpringApplication.run(WebApplication.class, args);
        }

        @FeignClient("name")
        static interface NameService {
            @RequestMapping("/")
            public String getName();
        }
    }

从官网的例子中,可以看出是通过注解驱动的,所以从注解开始看起。

spring cloud feign是如何工作的

Feign涉及了两个注解,一个是@EnableFeignClients,用来开启 Feign,另一个是@FeignClient,用来标记要用 Feign 来拦截的请求接口。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(FeignClientsRegistrar.class)
    public @interface EnableFeignClients {

        String[] value() default {};

        String[] basePackages() default {};

        Class<?>[] basePackageClasses() default {};

        Class<?>[] defaultConfiguration() default {};

        Class<?>[] clients() default {};
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FeignClient {

        @AliasFor("name")
        String value() default "";

        @Deprecated
        String serviceId() default "";

        @AliasFor("value")
        String name() default "";

        String qualifier() default "";

        String url() default "";

        boolean decode404() default false;

        Class<?>[] configuration() default {};

        Class<?> fallback() default void.class;

        Class<?> fallbackFactory() default void.class;

        String path() default "";

        boolean primary() default true;

    }

@EnableFeignClients 是关于注解扫描的配置,比如扫描路径,配置等。@FeignClient 则是关于对该接口进行代理的时候,一些实现细节的配置,比如访问url是什么, fallback 方法,关于404的请求是抛错误还是正常返回。

先关注对EnableFeignClients 的处理,可以看出它使用了@Import(FeignClientsRegistrar.class),看名字可知是一个注册器,通过扫描某个特性的类,将bean注册到IOC中。他的生命周期方法是registerBeanDefinitions ,Spring 通过调用其 registerBeanDefinitions 方法来获取其提供的 bean definition。

    public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
            //注册configuration
            registerDefaultConfiguration(metadata, registry);
            //注册注解
            registerFeignClients(metadata, registry);
        }
    private void registerDefaultConfigurati(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
        //获取注解@EnableFeignClients 下设置的属性值
        Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            //判断传入的defaultConfiguration的是不是topClass,所谓topClass就是说此类不是别的类的内部类
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            registerClientConfiguration(registry, name,defaultAttrs.get("defaultConfiguration"));
        }
    }
    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {
        //加载FeignClientSpecification bean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        //注册
        registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());
    }

这里会往 Registry 里面添加一个BeanDefinition,即 FeignClientSpecification,主要可调整项是通过 EnableFeignClients 注解的 defaultConfiguration 参数传入。

    public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
            ClassPathScanningCandidateComponentProvider scanner = getScanner();
            scanner.setResourceLoader(this.resourceLoader);

            Set<String> basePackages;

            Map<String, Object> attrs = metadata
                    .getAnnotationAttributes(EnableFeignClients.class.getName());
            // 扫描带有FeignClient注解的类
            AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                    FeignClient.class);
            //获取@EnableFeignClients 中clients的值
            final Class<?>[] clients = attrs == null ? null
                    : (Class<?>[]) attrs.get("clients");
            if (clients == null || clients.length == 0) {
                //如果没有设置,那么加入要扫描的注解和扫描的包
                scanner.addIncludeFilter(annotationTypeFilter);
                // 确定扫描的包路径列表
                basePackages = getBasePackages(metadata);
            }
            else {
                //如果设置了,最终扫出来的Bean必须是注解中设置的那些
                final Set<String> clientClasses = new HashSet<>();
                basePackages = new HashSet<>();
                for (Class<?> clazz : clients) {
                    basePackages.add(ClassUtils.getPackageName(clazz));
                    clientClasses.add(clazz.getCanonicalName());
                }
                AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                    @Override
                    protected boolean match(ClassMetadata metadata) {
                        String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                        return clientClasses.contains(cleaned);
                    }
                };
                scanner.addIncludeFilter(
                        new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
            }
            //循环扫描,并把根据注解信息,进行相关注册
            for (String basePackage : basePackages) {
                Set<BeanDefinition> candidateComponents = scanner
                        .findCandidateComponents(basePackage);
                for (BeanDefinition candidateComponent : candidateComponents) {
                    if (candidateComponent instanceof AnnotatedBeanDefinition) {
                        // verify annotated class is an interface
                        AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                        AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                        //必须注解在interface上
                        Assert.isTrue(annotationMetadata.isInterface(),
                                "@FeignClient can only be specified on an interface");

                        Map<String, Object> attributes = annotationMetadata
                                .getAnnotationAttributes(
                                        FeignClient.class.getCanonicalName());

                        String name = getClientName(attributes);
                        registerClientConfiguration(registry, name,attributes.get("configuration"));

                        registerFeignClient(registry, annotationMetadata, attributes);
                    }
                }
            }
        }

        private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
            String className = annotationMetadata.getClassName();
            BeanDefinitionBuilder definition = BeanDefinitionBuilder
                    .genericBeanDefinition(FeignClientFactoryBean.class);
            validate(attributes);
            //将属性设置到FeignClientFactoryBean 中
            definition.addPropertyValue("url", getUrl(attributes));
            definition.addPropertyValue("path", getPath(attributes));
            String name = getName(attributes);
            definition.addPropertyValue("name", name);
            definition.addPropertyValue("type", className);
            definition.addPropertyValue("decode404", attributes.get("decode404"));
            definition.addPropertyValue("fallback", attributes.get("fallback"));
            definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
            //设置Autowire 类型
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

            String alias = name + "FeignClient";
            AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

            boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null

            beanDefinition.setPrimary(primary);

            String qualifier = getQualifier(attributes);
            if (StringUtils.hasText(qualifier)) {
                alias = qualifier;
            }
            //注册bean
            BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                    new String[] { alias });
            BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        }

另一个往 Registry 里面添加的 BeanDefinition则是FeignClientFactoryBean,负责注册FeignClient。

也就是说,Feign的注册一共分为一下几步:

  1. 扫描@EnableFeignClients注解,如果有defaultConfiguration属性配置,则将configuration注册到BeanDefinition中,如果不指定的话,spring 提供的默认配置是FeignClientsConfiguration。
  2. 扫描 basePackage 下面所有包含了 FeignClient 注解的类
  3. 如果@EnableFeignClients中配置了clients属性,则扫描出来的bean只有在clients中配置的那些
  4. 循环扫描@FeignClient注解,如果配置了configuration,则将configuration按照 1 注册打BeanDefinition中,也就是说Feign既支持用作统一的默认的Config作为全局配置,也可以分别在@FeignClient中单独配置configuration 作为局部配置。
  5. 将@FeignClient中的其他配置设置到FeignClientFactoryBean中。

接下来来看下 FeignClientFactoryBean,它是一个工厂类,Spring Context 创建 Bean 实例时会调用它的 getObject 方法。

    public Object getObject() throws Exception {
            return getTarget();
        }

        /** * @param <T> the target type of the Feign client * @return a {@link Feign} client created with the specified data and the context information */
        <T> T getTarget() {
            FeignContext context = applicationContext.getBean(FeignContext.class);
            Feign.Builder builder = feign(context);

            if (!StringUtils.hasText(this.url)) {
                //如果没有指定url,获取name值拼接默认url
                String url;
                if (!this.name.startsWith("http")) {
                    url = "http://" + this.name;
                }
                else {
                    url = this.name;
                }
                url += cleanPath();
                return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
                        this.name, url));
            }
            if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
                this.url = "http://" + this.url;
            }
            String url = this.url + cleanPath();
            Client client = getOptional(context, Client.class);
            if (client != null) {
                if (client instanceof LoadBalancerFeignClient) {
                    //使用ribbon提供的负载均衡
                    // 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));
        }

如果指定了URL,没有指定client,那么就根据url来调用,相当于直连,没有负载均衡。如果没有指定client的话,可以使用负载均衡。现在的版本是默认开启负载均衡。

可以看出通过feign(context)方法初始化了Feign.Builder,所以着重看一下这个方法:

        protected Feign.Builder feign(FeignContext context) {
            //获取FeignClientsConfiguration 中注册的bean ,设置到feign中
            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;
        }

这里被设置到builder的bean来自于FeignClientsConfiguration在启动时加载到了context中,这是spring的默认配置,即使在@EnableFeignClients和@FeignClient没有配置configuration也能保证可以使用。

    @Configuration
    public class FeignClientsConfiguration {
        ...
        @Bean
        @ConditionalOnMissingBean
        public Decoder feignDecoder() {
            return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
        }

        @Bean
        @ConditionalOnMissingBean
        public Encoder feignEncoder() {
            return new SpringEncoder(this.messageConverters);
        }

        @Bean
        @ConditionalOnMissingBean
        public Contract feignContract(ConversionService feignConversionService) {
            return new SpringMvcContract(this.parameterProcessors, feignConversionService);
        }
        ...
    }

那么自定义的configuration在哪里加载呢,可以看到方法feign(context)中最后一行调用了configureFeign(context, builder),来看一下这个方法。

    protected void configureFeign(FeignContext context, Feign.Builder builder) {
            //获取.properties的属性
            FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
            if (properties != null) {
                if (properties.isDefaultToProperties()) {
                    //默认为true
                    configureUsingConfiguration(context, builder);
                    configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                    configureUsingProperties(properties.getConfig().get(this.name), builder);
                } else {
                    configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
                    configureUsingProperties(properties.getConfig().get(this.name), builder);
                    configureUsingConfiguration(context, builder);
                }
            } else {
                configureUsingConfiguration(context, builder);
            }
    }
    //获取用户通过configuration @Bean的自定义配置
        protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
            Logger.Level level = getOptional(context, Logger.Level.class);
            if (level != null) {
                builder.logLevel(level);
            }
            Retryer retryer = getOptional(context, Retryer.class);
            if (retryer != null) {
                builder.retryer(retryer);
            }
            ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
            if (errorDecoder != null) {
                builder.errorDecoder(errorDecoder);
            }
            //connectTimeoutMillis和readTimeoutMillis的默认值
            Request.Options options = getOptional(context, Request.Options.class);
            if (options != null) {
                builder.options(options);
            }
            Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
                    this.name, RequestInterceptor.class);
            if (requestInterceptors != null) {
                builder.requestInterceptors(requestInterceptors.values());
            }

            if (decode404) {
                builder.decode404();
            }
        }

        /*** * * @param config 获取.properties中配置的bean * @param builder feign */
        protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
            if (config == null) {
                return;
            }

            if (config.getLoggerLevel() != null) {
                builder.logLevel(config.getLoggerLevel());
            }
            //设置connectTimeoutMillis和readTimeoutMillis的值,这里的属性值来自于.properties配置的
            if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
                builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
            }

            if (config.getRetryer() != null) {
                Retryer retryer = getOrInstantiate(config.getRetryer());
                builder.retryer(retryer);
            }

            if (config.getErrorDecoder() != null) {
                ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
                builder.errorDecoder(errorDecoder);
            }

            if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
                // this will add request interceptor to builder, not replace existing
                for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
                    RequestInterceptor interceptor = getOrInstantiate(bean);
                    builder.requestInterceptor(interceptor);
                }
            }

            if (config.getDecode404() != null) {
                if (config.getDecode404()) {
                    builder.decode404();
                }
            }

            if (Objects.nonNull(config.getEncoder())) {
                builder.encoder(getOrInstantiate(config.getEncoder()));
            }

            if (Objects.nonNull(config.getDecoder())) {
                builder.decoder(getOrInstantiate(config.getDecoder()));
            }

            if (Objects.nonNull(config.getContract())) {
                builder.contract(getOrInstantiate(config.getContract()));
            }
        }

把配置文件中的配置项在启动时初始化到FeignClientProperties中。

  • 如果配置文件中没有配置,则将FeignClientsConfiguration中的bean作为默认值设置到builder。
  • 如果配置文件中有配置,并且用默认加载顺序时,首先加载FeignClientsConfiguration中的bean,然后加载在注解中配置的configuration,最后加载配置文件中的。
  • 如果不是默认加载顺序,则首先加载注解中配置的configuration,然后加载配置文件中的配置,最后加载FeignClientsConfiguration中的bean。注意,顺序在后面的配置会覆盖掉前面的

配置文件加载完之后,就是最关键的一步,获取 Targeter 来生成动态代理类,在 FeignAutoConfiguration 里面,配置了Target。

        protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
                HardCodedTarget<T> target) {
            Client client = getOptional(context, Client.class);
            if (client != null) {
                builder.client(client);
                Targeter targeter = get(context, Targeter.class);
                return targeter.target(this, builder, context, target);
            }

            throw new IllegalStateException(
                    "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
        }
    class HystrixTargeter implements Targeter {

        @Override
        public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                            Target.HardCodedTarget<T> target) {
            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);
            }

            return feign.target(target);
        }
    }

如果 FeignClient 没有定义 fallback,或者”feign.hystrix.HystrixFeign”路径不存在,则直接用 FeignBuidler 中DefaultTargeter的 target 方法生成代理。

        @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();
            }
        }
    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是怎么工作的

这里会直接调用Feign的target方法:

      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);
          ParseHandlersByName handlersByName =
              new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                                      errorDecoder, synchronousMethodHandlerFactory);
                //handlersByName将所有参数进行封装,并提供解析接口方法的逻辑
            //invocationHandlerFactory是Builder的属性,默认值是InvocationHandlerFactory.Default,用创建java动态代理的InvocationHandler实现
          return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
        }

ReflectiveFeign 生成动态代理对象(newInstance)。

    public <T> T newInstance(Target<T> target) {
        //为每个方法创建一个SynchronousMethodHandler对象,并放在 Map 里面。
        Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

        for (Method method : target.type().getMethods()) {
          if (method.getDeclaringClass() == Object.class) {
            continue;
          } else if(Util.isDefault(method)) {
            //如果是 default 方法,说明已经有实现了,用 DefaultHandler
            DefaultMethodHandler handler = new DefaultMethodHandler(method);
            defaultMethodHandlers.add(handler);
            methodToHandler.put(method, handler);
          } else {
            //否则就用上面的 SynchronousMethodHandler
            methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
          }
        }
        // 创建动态代理,factory 是 InvocationHandlerFactory.Default,创建出来的是 ReflectiveFeign.FeignInvocationHanlder,也就是说后续对方法的调用都会进入到该对象的 inovke 方法。
        InvocationHandler handler = factory.create(target, methodToHandler);
        // 创建动态代理对象
        T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

        for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
          defaultMethodHandler.bindTo(proxy);
        }
        return proxy;
      }

这段代码主要的逻辑是:

  1. 创建MethodHandler的映射,这里创建的是实现类SynchronousMethodHandler
  2. 通过InvocationHandlerFatory创建InvocationHandler
  3. 绑定接口的default方法,通过DefaultMethodHandler绑定

        public Map<String, MethodHandler> apply(Target key) {
            //取出要实现的接口的所有方法
          List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
          Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
          for (MethodMetadata md : metadata) {
              //根据目标接口类和方法上的注解信息判断该用哪种 buildTemplate
            BuildTemplateByResolvingArgs buildTemplate;
            if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
              buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
            } else if (md.bodyIndex() != null) {
              buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
            } else {
              buildTemplate = new BuildTemplateByResolvingArgs(md);
            }
              //调用synchronousMethodHandlerFactory来生成SynchronousMethodHandler对象。这个就是对接口某个方法的实现。
            result.put(md.configKey(),
                       factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
          }
          return result;
        }

这段代码的逻辑是:

  1. 通过Contract解析接口方法,生成MethodMetadata,默认的Contract解析Feign自定义的http注解
  2. 根据MethodMetadata方法元数据生成特定的RequestTemplate的工厂
  3. 使用SynchronousMethodHandler.Factory工厂创建SynchronousMethodHandler
    这里有两个工厂不要搞混淆了,SynchronousMethodHandler工厂和RequestTemplate工厂,SynchronousMethodHandler的属性包含RequestTemplate工厂
    synchronousMethodHandlerFactory 的 create 方法。
      public MethodHandler create(Target<?> target, MethodMetadata md,
                                    RequestTemplate.Factory buildTemplateFromArgs,
                                    Options options, Decoder decoder, ErrorDecoder errorDecoder) {
          return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
                                              logLevel, md, buildTemplateFromArgs, options, decoder,
                                              errorDecoder, decode404);
        }

ReflectiveFeign.FeignInvocationHanlder 的 invoke 方法

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          //通过动态代理实现了几个通用方法,比如 equals、toString、hasCode
          if ("equals".equals(method.getName())) {
            try {
              Object
                  otherHandler =
                  args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
              return equals(otherHandler);
            } catch (IllegalArgumentException e) {
              return false;
            }
          } else if ("hashCode".equals(method.getName())) {
            return hashCode();
          } else if ("toString".equals(method.getName())) {
            return toString();
          }
         //找到具体的 method 的 Handler,然后调用 invoke 方法。这样就又进入了SynchronousMethodHandler对象的 invoke 方法。
          return dispatch.get(method).invoke(args);
        }

SynchronousMethodHandler 的 invoke 方法主要是应用 encoder,decoder 以及 retry 等配置, 并且自身对于调用结果有一定的处理逻辑。

    public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();
        while (true) {
          try {
            return executeAndDecode(template);
          } catch (RetryableException e) {
            retryer.continueOrPropagate(e);
            if (logLevel != Logger.Level.NONE) {
              logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
          }
        }
      }

      Object executeAndDecode(RequestTemplate template) throws Throwable {
        Request request = targetRequest(template);

        if (logLevel != Logger.Level.NONE) {
          logger.logRequest(metadata.configKey(), logLevel, request);
        }

        Response response;
        long start = System.nanoTime();
        try {
          //通过 client 获得请求的返回值
          response = client.execute(request, options);
          // ensure the request is set. TODO: remove in Feign 10
          response.toBuilder().request(request).build();
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
          }
          throw errorExecuting(request, e);
        }
        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

        boolean shouldClose = true;
        try {
          if (logLevel != Logger.Level.NONE) {
            response =
                logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
            // ensure the request is set. TODO: remove in Feign 10
            response.toBuilder().request(request).build();
          }
          if (Response.class == metadata.returnType()) {
            if (response.body() == null) {
              return response;
            }
            if (response.body().length() == null ||
                    response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
              shouldClose = false;
              return response;
            }
            // Ensure the response body is disconnected
            byte[] bodyData = Util.toByteArray(response.body().asInputStream());
            return response.toBuilder().body(bodyData).build();
          }
          if (response.status() >= 200 && response.status() < 300) {
            if (void.class == metadata.returnType()) {
              return null;
            } else {
              return decode(response);
            }
          } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
            return decode(response);
          } else {
            throw errorDecoder.decode(metadata.configKey(), response);
          }
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
          }
          throw errorReading(request, response, e);
        } finally {
          if (shouldClose) {
            ensureClosed(response.body());
          }
        }

最后放一张读源码时画的脑图。
20191123100158\_1.png

关于代码的详细注释,我从官网fork了一份在自己的git上,git地址是:https://github.com/brotherJ1017/spring-cloud-openfeign/tree/2.0.x


来源:http://ddrv.cn/a/88268

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring-cloud-openFeign源码深度解析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏