Feign与Spring结合

王守钰 2020-12-08 19:12:10

SpringContract


public class SpringContract extends DeclarativeContract {

  static final String ACCEPT = "Accept";
  static final String CONTENT_TYPE = "Content-Type";

  public SpringContract() {
    // 处理类上的@RequestMapping注解
    registerClassAnnotation(RequestMapping.class, (requestMapping, data) -> {
      appendMappings(data, requestMapping.value());
      // 判断是否声明了请求方式
      if (requestMapping.method().length == 1)
        // 设置请求方式
        data.template().method(Request.HttpMethod.valueOf(requestMapping.method()[0].name()));
      // 设置生产者信息
      handleProducesAnnotation(data, requestMapping.produces());
      // 设置消费信息
      handleConsumesAnnotation(data, requestMapping.consumes());
    });
    // 处理方法上的@RequestMapping
    registerMethodAnnotation(RequestMapping.class, (requestMapping, data) -> {
      // 获取mapping数组
      String[] mappings = requestMapping.value();
      // 拼接地址
      appendMappings(data, mappings);

      if (requestMapping.method().length == 1)
        // 判断是否指定了请求方法
        data.template().method(Request.HttpMethod.valueOf(requestMapping.method()[0].name()));
    });
    // 处理方法上的@RequestMapping直接
    registerMethodAnnotation(GetMapping.class, (mapping, data) -> {
      // 拼接地址
      appendMappings(data, mapping.value());
      // 设置请求方法为get
      data.template().method(Request.HttpMethod.GET);
      // 设置生产者
      handleProducesAnnotation(data, mapping.produces());
      // 设置消费者
      handleConsumesAnnotation(data, mapping.consumes());
    });

    registerMethodAnnotation(PostMapping.class, (mapping, data) -> {
      appendMappings(data, mapping.value());
      // 设置请求方法为post
      data.template().method(Request.HttpMethod.POST);
      handleProducesAnnotation(data, mapping.produces());
      handleConsumesAnnotation(data, mapping.consumes());
    });

    registerMethodAnnotation(PutMapping.class, (mapping, data) -> {
      appendMappings(data, mapping.value());
      // 设置请求方法为put
      data.template().method(Request.HttpMethod.PUT);
      handleProducesAnnotation(data, mapping.produces());
      handleConsumesAnnotation(data, mapping.consumes());
    });

    registerMethodAnnotation(DeleteMapping.class, (mapping, data) -> {
      appendMappings(data, mapping.value());
      // 设置请求方法为delete
      data.template().method(Request.HttpMethod.DELETE);
      handleProducesAnnotation(data, mapping.produces());
      handleConsumesAnnotation(data, mapping.consumes());
    });

    registerMethodAnnotation(PatchMapping.class, (mapping, data) -> {
      appendMappings(data, mapping.value());
      // 设置请求方法为patch
      data.template().method(Request.HttpMethod.PATCH);
      handleProducesAnnotation(data, mapping.produces());
      handleConsumesAnnotation(data, mapping.consumes());
    });

    registerMethodAnnotation(ResponseBody.class, (body, data) -> {
      // 如果响应结果上有@ResponseBody注解,设置消费者数据为application/json类型
      handleConsumesAnnotation(data, "application/json");
    });
    // 处理@ExceptionHandler注解
    registerMethodAnnotation(ExceptionHandler.class, (ann, data) -> {
      // 忽略方法
      data.ignoreMethod();
    });
    // 处理参数上的@PathVariable注解
    registerParameterAnnotation(PathVariable.class, (parameterAnnotation, data, paramIndex) -> {
      // 获取参数值
      String name = PathVariable.class.cast(parameterAnnotation).value();
      // 处理参数名称
      nameParam(data, name, paramIndex);
    });
    // 处理请求参数上的@RequestBody注解
    registerParameterAnnotation(RequestBody.class, (body, data, paramIndex) -> {
      // 设置请求类型为application/json
      handleProducesAnnotation(data, "application/json");
    });
    // 处理请求参数上的@RequestParam注解
    registerParameterAnnotation(RequestParam.class, (parameterAnnotation, data, paramIndex) -> {
      // 请求参数值
      String name = RequestParam.class.cast(parameterAnnotation).value();
      // 添加模板参数
      Collection<String> query = addTemplatedParam(data.template().queries().get(name), name);
      // 设置query属性
      data.template().query(name, query);
      // 设置参数值
      nameParam(data, name, paramIndex);
    });

  }

  private void appendMappings(MethodMetadata data, String[] mappings) {
    for (int i = 0; i < mappings.length; i++) {
      String methodAnnotationValue = mappings[i];
      if (!methodAnnotationValue.startsWith("/") && !data.template().url().endsWith("/")) {
        methodAnnotationValue = "/" + methodAnnotationValue;
      }
      if (data.template().url().endsWith("/") && methodAnnotationValue.startsWith("/")) {
        methodAnnotationValue = methodAnnotationValue.substring(1);
      }

      data.template().uri(data.template().url() + methodAnnotationValue);
    }
  }

  private void handleProducesAnnotation(MethodMetadata data, String... produces) {
    if (produces.length == 0)
      return;
    data.template().removeHeader(ACCEPT); // remove any previous produces
    data.template().header(ACCEPT, produces[0]);
  }

  private void handleConsumesAnnotation(MethodMetadata data, String... consumes) {
    if (consumes.length == 0)
      return;
    data.template().removeHeader(CONTENT_TYPE); // remove any previous consumes
    data.template().header(CONTENT_TYPE, consumes[0]);
  }

  protected Collection<String> addTemplatedParam(Collection<String> possiblyNull, String name) {
    if (possiblyNull == null) {
      possiblyNull = new ArrayList<String>();
    }
    possiblyNull.add(String.format("{%s}", name));
    return possiblyNull;
  }

}

在SpringContract进行实例化的时候注册了RequestMapping、GetMapping、PostMapping、PutMapping、DeleteMapping、PatchMapping、ResponseBody、ExceptionHandler、PathVariable、RequestBody、RequestParam的注解信息。其他的逻辑还是不变,在构建Feign的信息时,设置Spring的Contract即可实现对Spring的注解解析。