源码解析: Feign Contract

Contract 父接口

Contract的中文意义是合约,parseAndValidatateMetadata的作用顾名思义就是将我们传入的接口进行解析验证,看注解的使用是否符合规范,然后返回给我们接口上各种相应的元数据。

BaseContract 子类

其主要逻辑是验证我们使用的时候是否符合了规范。
抽取元数据的逻辑留给了子类

@Override
    public List 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 result = new LinkedHashMap();
      //取得接口里面的所有public方法,包括从父接口继承而来的
      for (Method method : targetType.getMethods()) {
      //排除掉从Object继承的方法,static方法,接口中的default方法
        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(result.values());
    }
解析接口中的单个方法
protected MethodMetadata parseAndValidateMetadata(Class targetType, Method method) {
      //方法元数据封装类
      MethodMetadata data = new MethodMetadata();
      //解析出方法的返回类型,好复杂???
      data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
      //用Feign中的静态方法configKey解析出接口&方法作为一种String key,为什么要放在Feign类中
      data.configKey(Feign.configKey(targetType, method));
      //如果传入的接口有一个父接口,处理父接口上的注解,放入MethodMetadata
      if(targetType.getInterfaces().length == 1) {
        processAnnotationOnClass(data, targetType.getInterfaces()[0]);
      }
      //处理传入的这个接口上自己的注解
      processAnnotationOnClass(data, targetType);
      //注意这个方法虚方法,留给了子类去实现。
      
      //处理这个方法上的注解,同样是虚方法
      for (Annotation methodAnnotation : method.getAnnotations()) {
        processAnnotationOnMethod(data, methodAnnotation, method);
      }
      //检查data里面template是否已放入了这个方法关于HHTP method的信息,比如时GET方法,POST方法之类的。这个操作之前应该是由processAnnotationOnMethod去实现的。
      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();
      //得到方法的参数上的在注解
      Annotation[][] parameterAnnotations = method.getParameterAnnotations();
     //遍历参数的注解
      int count = parameterAnnotations.length;
      for (int i = 0; i < count; i++) {
        boolean isHttpAnnotation = false;
        //是否是一个“http注解”?
        if (parameterAnnotations[i] != null) {
          isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
        }
        //如果参数是URL类型的,data中放入urlIndex?
        if (parameterTypes[i] == URI.class) {
          data.urlIndex(i);
        } else if (!isHttpAnnotation) {
          //如果“http注解”为false,检查data中formParams“表单参数”如果不为空,则报错。
          //body参数不能和form参数一起?????
          checkState(data.formParams().isEmpty(),
                     "Body parameters cannot be used with form parameters.");
          //如果data中bodyIndex不为空,???????
          checkState(data.bodyIndex() == null, "Method has too many Body parameters: %s", method);
          //设置bodyIndex
          data.bodyIndex(i);
          //设置bodyType????
          data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
        }
      }
      //检查headerMap所在的参数必须是一个Map类型,key为String
      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;
    }

Default 孙类 = =!

实现了以下3个方法的具体逻辑,负责抽取元信息
processAnnotationOnClass
processAnnotationOnMethod
processAnnotationsOnParameter

 @Override
    protected void processAnnotationOnClass(MethodMetadata data, Class targetType) {
      //检查接口上是否有Headers注解
      if (targetType.isAnnotationPresent(Headers.class)) {
        //将Headers里的信息转换成Map,“headers”
        String[] headersOnType = targetType.getAnnotation(Headers.class).value();
        checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.",
                   targetType.getName());
        Map> headers = toMap(headersOnType);
        //将template里面已有的headers也加入进来,合并后放入template里面。
        headers.putAll(data.template().headers());
        data.template().headers(null); // to clear
        data.template().headers(headers);
      }
    }
@Override
    protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation,
                                             Method method) {
      //提取注解的类型
      Class annotationType = methodAnnotation.annotationType();
      //如果是RequestLine注解
      if (annotationType == RequestLine.class) {
        //强转为RequestLine类型后取得,value属性的值
        String requestLine = RequestLine.class.cast(methodAnnotation).value();
        //检查requestLine的值
        checkState(emptyToNull(requestLine) != null,
                   "RequestLine annotation was empty on method %s.", method.getName());
        //如果没找到值里面有空格,比如“GET /mock/resource”
        if (requestLine.indexOf(' ') == -1) {
          //也没有找到"/",说明没有正确写值,报错
          checkState(requestLine.indexOf('/') == -1,
              "RequestLine annotation didn't start with an HTTP verb on method %s.",
              method.getName());
          //requestline放入template中,返回。
          data.template().method(requestLine);
          return;
        }
        //如果包含了空格,将空格前的http方法截取出来放入template中
        data.template().method(requestLine.substring(0, requestLine.indexOf(' ')));
        //根据是否包含了多个空格,去判断是否需要加上http的版本号,
        //比如“POST /servers HTTP/1.1”
        if (requestLine.indexOf(' ') == requestLine.lastIndexOf(' ')) {
          // no HTTP version is ok
          data.template().append(requestLine.substring(requestLine.indexOf(' ') + 1));
        } else {
          // skip HTTP version
          data.template().append(
              requestLine.substring(requestLine.indexOf(' ') + 1, requestLine.lastIndexOf(' ')));
        }

      //解析出decodeSlash,是否解码“/”?????        
   data.template().decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash());

      } else if (annotationType == Body.class) {//处理另一个可能出现在方法上的注解“Body”
        String body = Body.class.cast(methodAnnotation).value();
        checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.",
                   method.getName());
        //body注解的作用是让我们在PUT或者post时自定义请求体,里面的参数用花括号包裹
        if (body.indexOf('{') == -1) {
          //如果没有花括号,说明没有参数替换的必要,直接塞到Template的body里面去
          data.template().body(body);
        } else {
          //有,说明是个模板,先塞到模板属性里面去
          data.template().bodyTemplate(body);
        }
      } else if (annotationType == Headers.class) {
      //方法上也有可能有Headers注解,继续一波类似的骚操作。
      //记得之前在processAnnotationOnClass中的逻辑么
        String[] headersOnMethod = Headers.class.cast(methodAnnotation).value();
        checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.",
                   method.getName());
        data.template().headers(toMap(headersOnMethod));
      }
    }
@Override
    protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations,
                                                    int paramIndex) {
      //isHttpAnnotation
      boolean isHttpAnnotation = false;
      for (Annotation annotation : annotations) {
        Class annotationType = annotation.annotationType();
        if (annotationType == Param.class) {
          //同样是强转,这次的写法却不同……难道不是一个人写的
          Param paramAnnotation = (Param) annotation;
          String name = paramAnnotation.value();
          checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.", paramIndex);
          //将注解里的值塞到nameToIndex Map中
          nameParam(data, name, paramIndex);
          Class expander = paramAnnotation.expander();
          //如果重写了Expander,同理塞入
          if (expander != Param.ToStringExpander.class) {
            data.indexToExpanderClass().put(paramIndex, expander);
          }
          //注解声明参数的值是否已经编码过了
          data.indexToEncoded().put(paramIndex, paramAnnotation.encoded());
          //flag设置为true
          isHttpAnnotation = true;
          String varName = '{' + name + '}';
          //如果url中包含了我们方法上的参数名字 作为占位符,或者queries参数,或者headers头里面,将此值放入formParams
          if (!data.template().url().contains(varName) &&
              !searchMapValuesContainsSubstring(data.template().queries(), varName) &&
              !searchMapValuesContainsSubstring(data.template().headers(), varName)) {
            data.formParams().add(name);
          }
        } else if (annotationType == QueryMap.class) {
          //QueryMap的作用就是为我们提供一个无限量塞URL查询参数的地方,所以也保存他的元数据,同时设置flag为true
          checkState(data.queryMapIndex() == null, "QueryMap annotation was present on multiple parameters.");
          data.queryMapIndex(paramIndex);
          data.queryMapEncoded(QueryMap.class.cast(annotation).encoded());
          isHttpAnnotation = true;
        } else if (annotationType == HeaderMap.class) {
          //同理设置HeaderMap,flag
          checkState(data.headerMapIndex() == null, "HeaderMap annotation was present on multiple parameters.");
          data.headerMapIndex(paramIndex);
          isHttpAnnotation = true;
        }
      }
      //所以只有跟Feign相关的三个注解才叫isHttpAnnotation=true。并且处理相关元数据,其他的注解则啥也不干,返回false
      return isHttpAnnotation;
    }

总结

至此,通过Contract,BaseContract,Default 爷孙仨人的模板模式共同完成了一个任务,
将我们定义的API接口“targetType”中包含的所有关于HTTP的附加元信息抽取成结果集返回。

你可能感兴趣的:(源码解析: Feign Contract)