Feign源码解析之代理类的处理逻辑

/ Java / 没有评论 / 1567浏览

Feign源码解析之代理类的处理逻辑

前篇

正文

上面两篇文章分析了在springboot中feign是如何注入IOC容器并生成jdk动态代理类的,下面我们将着重分析一下feign的代理类的处理逻辑,看一看代理类是如何将接口里的方法转化成http请求的。

之前的文章有提到,在通过FeignClientFactoryBean的getObject方法生成代理类的过程中是根据applicationContext和配置文件获取到其中的属性值的,因此我们可以通过将特定类注入IOC容器或者修改配置的方式实现自定义的实现方式。所以,在文章开始之前首先声明,下文中的分析全部基于springboot中的feign的默认实现。

一. Feign的配置类

首先,feign在springboot中主要有两个配置类,其中的自动配置类FeignAutoConfiguration在之前的文章中已经有提到,下面我们了解一下另一个配置类FeignClientsConfiguration,这个类是在FeignContext的父类方法createContext中的context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);语句注入各个client所属的applicationContext中的。

@Configuration
public class FeignClientsConfiguration {

  @Autowired
  private ObjectFactory<HttpMessageConverters> messageConverters;

  @Autowired(required = false)
  private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();

  @Autowired(required = false)
  private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();

  @Autowired(required = false)
  private Logger logger;

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

  @Bean
  public FormattingConversionService feignConversionService() {
    FormattingConversionService conversionService = new DefaultFormattingConversionService();
    for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
      feignFormatterRegistrar.registerFormatters(conversionService);
    }
    return conversionService;
  }

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

  @Bean
  @ConditionalOnMissingBean
  public Retryer feignRetryer() {
    return Retryer.NEVER_RETRY;
  }

  @Bean
  @Scope("prototype")
  @ConditionalOnMissingBean
  public Feign.Builder feignBuilder(Retryer retryer) {
    return Feign.builder().retryer(retryer);
  }

  @Bean
  @ConditionalOnMissingBean(FeignLoggerFactory.class)
  public FeignLoggerFactory feignLoggerFactory() {
    return new DefaultFeignLoggerFactory(logger);
  }
}

FeignClientsConfiguratiob中注入的几个类都是在生成并使用代理类过程中的属性的默认值。

二. FeignInvocationHandler类的invoke方法

接着我们继续看动态类的执行方法:FeignInvocationHandler类的invoke方法,可以发现其执行逻辑为从dispatch中根据method获取到对应的methodHandle以执行invoke方法。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  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();
  }
  return dispatch.get(method).invoke(args);
}

由于dispatch属性是由ReflectiveFeign类调用FeignInvocationHandle的构造方法时传入的,我们需要进入ReflectiveFeign的newInstance方法关注dispatch的值。

public <T> T newInstance(Target<T> target) {
  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)) {
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      defaultMethodHandlers.add(handler);
      methodToHandler.put(method, handler);
    } else {
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  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;
}

局部变量methodToHandler即传入FeignInvocationHandle的dispatch属性,遍历feignClient接口的方法进行组装,分3中情况:

  1. 从object类继承而来的方法,忽略。这一点也和FeignInvocationHandle的invoke方法针对equal、hashcode、toString方法的处理吻合。
  2. 接口中的default方法,对应的methodHandle的逻辑依旧还是原方法的逻辑
  3. 其它方法,也是我们最关注的需要进行http请求处理的方法,根据方法的key值从nameToHandler中获取。

可以先了解一下方法的key值是怎么生成的:方法并不复杂,由3部分组成:接口名、方法名、每个方法参数的类型,从而保证了唯一性。

public static String configKey(Class targetType, Method method) {
  StringBuilder builder = new StringBuilder();
  builder.append(targetType.getSimpleName());
  builder.append('#').append(method.getName()).append('(');
  for (Type param : method.getGenericParameterTypes()) {
    param = Types.resolve(targetType, targetType, param);
    builder.append(Types.getRawType(param).getSimpleName()).append(',');
  }
  if (method.getParameterTypes().length > 0) {
    builder.deleteCharAt(builder.length() - 1);
  }
  return builder.append(')').toString();
}

三. Feign方法到MethodMetaData

接着看apply方法是如何生成nameToHandles的,在apply方法里又调用了contract的parseAndValidatateMetadata方法,在springboot项目中这儿contract默认的contract是SpringMvcContract类,parseAndValidatateMetadata在其父类BaseContract中。

public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
  checkState( targetType.getTypeParameters().length == 0, 
             "Parameterized types unsupported: %s",
             targetType.getSimpleName());
  checkState( targetType.getInterfaces().length <= 1,
             "Only single inheritance supported: %s",
             targetType.getSimpleName());
  if (targetType.getInterfaces().length == 1) {
    checkState(targetType.getInterfaces()[0].getInterfaces().length == 0,
               "Only single-level inheritance supported: %s",
               targetType.getSimpleName());
  }
  Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
  for (Method method : targetType.getMethods()) {
    if (method.getDeclaringClass() == Object.class ||
        (method.getModifiers() & Modifier.STATIC) != 0 ||
        Util.isDefault(method)) {
      continue;
    }
    MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
    checkState(!result.containsKey(metadata.configKey()), 
               "Overrides unsupported: %s",
               metadata.configKey());
    result.put(metadata.configKey(), metadata);
  }
  return new ArrayList<MethodMetadata>(result.values());
}

可以看出,该方法的,目的是对接口内的每个方法进行解析,并封装成MethodMetadata,放入容器list内返回。该方法先进行了前置校验,对接口进行校验,要求:

  1. 接口不是泛型接口,不含有类型参数。
  2. 接口最多只有1个父类接口,且其父类接口不能再含有父类接口。

接着遍历方法,依次获取并封装每个方法的MethodMetadata,并做一次后置校验,防止重复。同样的,从object类继承的方法、static方法和default方法不处理。

进入同名方法parseAndValidateMetadata,注意SpringMvcContract对该方法进行了重写,当然SpringMvcContract重写后的方法还是先调用了MethodMetadata md = super.parseAndValidateMetadata(targetType, method);以先执行BaseContract中的对应方法,先看BaseContract的parseAndValidateMetadata方法。

protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
  MethodMetadata data = new MethodMetadata();
  //1.returnType和configKey的属性设置
  data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
  data.configKey(Feign.configKey(targetType, method));

  //2.接口注解的解析
  if(targetType.getInterfaces().length == 1) {
    processAnnotationOnClass(data, targetType.getInterfaces()[0]);
  }
  processAnnotationOnClass(data, targetType);

  //3.方法注解的解析
  for (Annotation methodAnnotation : method.getAnnotations()) {
    processAnnotationOnMethod(data, methodAnnotation, method);
  }
  checkState(data.template().method() != null,
             "Method %s not annotated with HTTP method type (ex. GET, POST)",
             method.getName());
  Class<?>[] parameterTypes = method.getParameterTypes();
  Type[] genericParameterTypes = method.getGenericParameterTypes();

  //4.参数和参数注解的解析
  Annotation[][] parameterAnnotations = method.getParameterAnnotations();
  int count = parameterAnnotations.length;
  for (int i = 0; i < count; i++) {
    boolean isHttpAnnotation = false;
    if (parameterAnnotations[i] != null) {
      isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
    }
    if (parameterTypes[i] == URI.class) {
      data.urlIndex(i);
    } else if (!isHttpAnnotation) {
      checkState(data.formParams().isEmpty(),
                 "Body parameters cannot be used with form parameters.");
      checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method);
      data.bodyIndex(i);
      data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
    }
  }

  if (data.headerMapIndex() != null) {
    checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]);
  }

  if (data.queryMapIndex() != null) {
    checkMapString("QueryMap", parameterTypes[data.queryMapIndex()], genericParameterTypes[data.queryMapIndex()]);
  }

  return data;
}

这个方法里我们开始了MethodMetadata的初始化,并初步开始进行封装,一共可以分为4步,我们逐一解析。

1. returnType和configKey的属性设置

这两个属性比较简单,从method中获取即可。

2. 接口注解的解析

如果接口上有RequestMapping注解,且value属性值不为空,取其value值作为RequestTemplate中url的前缀,这一点和我们的使用习惯也是吻合的,应该比较好理解。

@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
  if (clz.getInterfaces().length == 0) {
    RequestMapping classAnnotation = findMergedAnnotation(clz,
                                                          RequestMapping.class);
    if (classAnnotation != null) {
      // Prepend path from class annotation if specified
      if (classAnnotation.value().length > 0) {
        String pathValue = emptyToNull(classAnnotation.value()[0]);
        pathValue = resolve(pathValue);
        if (!pathValue.startsWith("/")) {
          pathValue = "/" + pathValue;
        }
        data.template().insert(0, pathValue);
      }
    }
  }
}

3. 方法注解的解析

方法上需要进行解析的依旧还是RequestMapping注解。 在这一部里,我们对RequestMapping进行解析得到method、path、produces、consumes、headers属性。

@Override
protected void processAnnotationOnMethod(MethodMetadata data,
                                         Annotation methodAnnotation, Method method) {
  if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
      .annotationType().isAnnotationPresent(RequestMapping.class)) {
    return;
  }

  RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
  // HTTP Method
  RequestMethod[] methods = methodMapping.method();
  if (methods.length == 0) {
    methods = new RequestMethod[] { RequestMethod.GET };
  }
  checkOne(method, methods, "method");
  data.template().method(methods[0].name());

  // path
  checkAtMostOne(method, methodMapping.value(), "value");
  if (methodMapping.value().length > 0) {
    String pathValue = emptyToNull(methodMapping.value()[0]);
    if (pathValue != null) {
      pathValue = resolve(pathValue);
      // Append path from @RequestMapping if value is present on method
      if (!pathValue.startsWith("/")
          && !data.template().toString().endsWith("/")) {
        pathValue = "/" + pathValue;
      }
      data.template().append(pathValue);
    }
  }

  // produces
  parseProduces(data, method, methodMapping);

  // consumes
  parseConsumes(data, method, methodMapping);

  // headers
  parseHeaders(data, method, methodMapping);

  data.indexToExpander(new LinkedHashMap<Integer, Param.Expander>());
}

3.1 method

对应RequestMapping的method属性,对应RequestMapping的method属性。要求RequestMapping最多只能指定1个method属性,如果没有指定,则默认为GET请求。

3.2 path

对应RequestMapping的value属性,对应RequestMapping的url属性。同样要求RequestMapping最多只能指定1个value属性。

3.3 produces

对应RequestMapping的produces属性,对应RequestTemplate的headers的accept属性。

private void parseProduces(MethodMetadata md, Method method,
                           RequestMapping annotation) {
  String[] serverProduces = annotation.produces();
  String clientAccepts = serverProduces.length == 0 ? null
    : emptyToNull(serverProduces[0]);
  if (clientAccepts != null) {
    md.template().header(ACCEPT, clientAccepts);
  }
}

3.4 consumes

对应RequestMapping的consumes属性,对应RequestTemplate的headers的Content-Type属性。

private void parseConsumes(MethodMetadata md, Method method,
                           RequestMapping annotation) {
  String[] serverConsumes = annotation.consumes();
  String clientProduces = serverConsumes.length == 0 ? null
    : emptyToNull(serverConsumes[0]);
  if (clientProduces != null) {
    md.template().header(CONTENT_TYPE, clientProduces);
  }
}

3.5 headers

对应RequestMapping的headers属性,对应RequestTemplate的headers属性。 注意注释TODO: only supports one header value per key,每个key只能有1个value,因此只支持“key = value”格式的表达式,不支持“key != value”格式的表达式。

private void parseHeaders(MethodMetadata md, Method method,
                          RequestMapping annotation) {
  // TODO: only supports one header value per key
  if (annotation.headers() != null && annotation.headers().length > 0) {
    for (String header : annotation.headers()) {
      int index = header.indexOf('=');
      if (!header.contains("!=") && index >= 0) {
        md.template().header(resolve(header.substring(0, index)),
                             resolve(header.substring(index + 1).trim()));
      }
    }
  }
}

3.6 methodMetadata的indexToExpander设置为空的LinkedHashMap。

4.参数和参数注解的解析

接口和方法注解解析得到的数据主要针对methodMetadata中的RequesTtemplate属性,参数和参数注解的解析得到的数据只要针对methodMetadata中的其它属性。

由于每个参数都可以有多个注解,因此method.getParameterAnnotations()得到的是一个二维数组,接下来我们进入循环对每个参数进行遍历解析。

4.1 参数注解解析

进入processAnnotationsOnParameter方法,方法的返回值为布尔值,字面意思是该参数是否有http相关的注解,表示该参数是否经过参数处理器处理。 在分析方法之前,先了解一下方法中用到的几个SpringMvcContract类的成员变量。

processedMethods
method的feign的key值和method组成的映射对,在进入SpringMvcContract的parseAndValidateMetadata方法时通过this.processedMethods.put(Feign.configKey(targetType, method), method);语句进入对应的元素。
annotatedArgumentProcessors
注解类型和AnnotatedParameterProcessor类组成的映射对,用于feign方法参数的处理,在构造方法设值,项目中调用构造方法时传入的是一个empty list,所以此时的annotatedArgumentProcessors通过getDefaultAnnotatedArgumentsProcessors方法获得,对应@pathVariable、@RequestParam、@RequestHeader 3种注解及其对应的处理器
public SpringMvcContract(
  List<AnnotatedParameterProcessor> annotatedParameterProcessors,
  ConversionService conversionService) {
  Assert.notNull(annotatedParameterProcessors,
                 "Parameter processors can not be null.");
  Assert.notNull(conversionService, "ConversionService can not be null.");

  List<AnnotatedParameterProcessor> processors;
  if (!annotatedParameterProcessors.isEmpty()) {
    processors = new ArrayList<>(annotatedParameterProcessors);
  }
  else {
    processors = getDefaultAnnotatedArgumentsProcessors();
  }
  this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
  this.conversionService = conversionService;
  this.expander = new ConvertingExpander(conversionService);
}
private List<AnnotatedParameterProcessor> getDefaultAnnotatedArgumentsProcessors() {

  List<AnnotatedParameterProcessor> annotatedArgumentResolvers = new ArrayList<>();

  annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
  annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
  annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());

  return annotatedArgumentResolvers;
}

conversionService

同样在构造方法内设值,在调用构造方法时传入值,用于类型转换,对应FeignClientsConfiguration类中注入的FormattingConversionService类

expander

同样在构造方法内设值,this.expander = new ConvertingExpander(conversionService);

在了解了上面的几个成员变量之后,下面的代码应该就比较容易理解了,分两步: 4.1.1 根据注解类型查找参数处理器并解析,只有有1个注解解析成功则isHttpAnnotation 为true。参数处理器的处理逻辑并不复杂,不再解析。 4.1.2 isHttpAnnotation 为true,在methodMetadata的indexToExpander加入expander。

@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data,
                                                Annotation[] annotations, int paramIndex) {
  boolean isHttpAnnotation = false;

  AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
    data, paramIndex);
  Method method = this.processedMethods.get(data.configKey());
  for (Annotation parameterAnnotation : annotations) {
    AnnotatedParameterProcessor processor = this.annotatedArgumentProcessors
      .get(parameterAnnotation.annotationType());
    if (processor != null) {
      Annotation processParameterAnnotation;
      // synthesize, handling @AliasFor, while falling back to parameter name on
      // missing String #value():
      processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
        parameterAnnotation, method, paramIndex);
      isHttpAnnotation |= processor.processArgument(
        context, processParameterAnnotation, method);
    }
  }
  if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
      && this.conversionService.canConvert(
        method.getParameterTypes()[paramIndex], String.class)) {
    data.indexToExpander().put(paramIndex, this.expander);
  }
  return isHttpAnnotation;
}

4.2 参数类型解析

如果参数类型是URI类型,设置methodMetaData的urlIndex属性

4.3 RequestBody 处理

isHttpAnnotation为false,即如果该参数没有经过参数处理器处理,将其按RequestBody 处理,设置bodyIndex。 需要注意的是两个校验条件,body不能喝form同时使用,最多只能用一个body。

5. headerMapIndex 校验

必须为Map格式,key必须为String,这一点其实对应方法中类似于@requestParam Map<String, Object> params的参数

6. queryMapIndex 校验

必须为Map格式,key必须为String,这一点其实对应方法中类似于@requestHeader Map<String, Object> params的参数。

回到SpringMvcContract的parseAndValidateMetadata方法,我们接着分析在执行完MethodMetadata md = super.parseAndValidateMetadata(targetType, method);后的操作。可以看出当方法的注解RequestMapping没有指定produces、consumes和headers时,采用接口注解RequestMapping上的对应属性。

@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
  this.processedMethods.put(Feign.configKey(targetType, method), method);
  MethodMetadata md = super.parseAndValidateMetadata(targetType, method);

  RequestMapping classAnnotation = findMergedAnnotation(targetType,
                                                        RequestMapping.class);
  if (classAnnotation != null) {
    // produces - use from class annotation only if method has not specified this
    if (!md.template().headers().containsKey(ACCEPT)) {
      parseProduces(md, method, classAnnotation);
    }

    // consumes -- use from class annotation only if method has not specified this
    if (!md.template().headers().containsKey(CONTENT_TYPE)) {
      parseConsumes(md, method, classAnnotation);
    }

    // headers -- class annotation is inherited to methods, always write these if
    // present
    parseHeaders(md, method, classAnnotation);
  }
  return md;
}

至此,Feign方法到MethodMetaData的封装完成,我们回到上一步,ParseHandlersByName类的apply方法。

四. MethodMetaData到SynchronousMethodHandler

在得到一个接口下所有feign方法组成的MethodMetaData的list后,遍历,分成有form数据、body数据和没有form和body数据3种情况继续封装成BuildFormEncodedTemplateFromArgs、BuildEncodedTemplateFromArgs和BuildTemplateByResolvingArg,其两者是第三者的子类,重写了第三者的resolve方法,得到RequestTemplate的方法不同,然后生成SynchronousMethodHandler类。

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) {
    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);
    }
    result.put(md.configKey(),
               factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
  }
  return result;
}
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);
}

五. SynchronousMethodHandler 的 invoke 方法

从前面的分析可以得到,我们调用feign接口的方法,进入了FeignInvocationHandle的invoke方法,而在FeignInvocationHandle的invoke里,对于除了object继承而来的方法和default方法以外的feign方法,都会进入SynchronousMethodHandler的invoke方法。

根据生成的参数和封装了MethodMetaDatabuildTemplateFromArgs类得到了RequestTemplate,委托executeAndDecode(template)方法进行发送请求的逻辑,retryer.continueOrPropagate(e)控制了重发逻辑。

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

接着看executeAndDecode方法,

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

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

  Response response;
  long start = System.nanoTime();
  try {
    //调用client进行发送,得到响应response
    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();
    }
    //由Response到方法返回值
    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());
    }
  }
}

除去方法中的打印日志和异常捕捉部分,主要逻辑有3步:

1. 由RequestTemplate到Request

在这里,用户可以往IOC容器注入RequestInterceptor类,来对http请求做一些公共的处理。

Request targetRequest(RequestTemplate template) {
  for (RequestInterceptor interceptor : requestInterceptors) {
    interceptor.apply(template);
  }
  return target.apply(new RequestTemplate(template));
}

2.调用client进行发送,得到响应response

调用clinet发送请求,LoadBalancerFeignClient可以处理负载均衡

3.由Response到方法返回值

又细分为以下几种情况:

  1. 返回值类型为Response
  1. response.status [200, 300)
  1. decode404 && response.status() == 404 && void.class != metadata.returnType() 执行decode.decode进行解码并返回。
  2. 执行errorDecoder.decode进行异常处理,抛出异常。

4.确保连接关闭