Retrofit源碼解析二——對接口方法註解的處理

首先經過Utils.validateServiceInterface(service);來檢查傳入的參數是不是一個沒有繼承關係的接口,這是怎麼判斷的呢?java

static <T> void validateServiceInterface(Class<T> service) {
    if (!service.isInterface()) {//1.判斷是不是interface
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    if (service.getInterfaces().length > 0) {//2.判斷是否實現了接口
      throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
    }
  }

而後eagerlyValidateMethods(service)是什麼鬼?正則表達式

private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
        loadServiceMethod(method);
      }
    }
  }

其實這個方法中,主要是對service中的全部方法作了檢查,不容許存在靜態方法。數組

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

接下來就是把反射出來的方法緩存,其中緩存的方式是調用ServiceMethod.parseAnnotations(this, method);把method轉換成RequestFactory對象:緩存

final class RequestFactory {
  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
  ...

咱們看這裏的builder都幹了哪些勾當,首先看構造方法:網絡

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
  • method.getAnnotations():獲取方法的註解數組。
  • method.getGenericParameterTypes();獲取型參的類型數組,注意是類型。
  • method.getParameterAnnotations();獲取型參的註解數組。

而後再看build()方法,乾的勾當更多:post

RequestFactory build() {
      for (Annotation annotation : methodAnnotations) {
	  //判斷加了哪些註解,肯定網絡傳輸方式
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(method,
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError(method, "FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError(method, "Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError(method, "Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError(method, "Multipart method must contain at least one @Part.");
      }

      return new RequestFactory(this);
    }
/**
*	這一步主要是用來判斷加了哪些註解
**/
private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError(method, "@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }
/**
*	肯定網絡傳輸方式,如get、post、delete
*	而且提取出了真實的url和url中攜帶的參數名稱
**/
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
	//檢測httpmethod,不容許加多個http方法註解
      if (this.httpMethod != null) {
        throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }
	  //肯定了網絡傳輸方法,好比get、post、delete等
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
	  //這裏是用正則表達式作判斷,要求url中的參數必須用括號佔位
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError(method, "URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }
	//獲取真實的url
      this.relativeUrl = value;
	  //從url中攝取參數名稱
      this.relativeUrlParamNames = parsePathParameters(value);
    }

看到這裏,我發現retrofit是支持HERDER請求的,固然,在if (annotation instanceof retrofit2.http.Headers)部分的邏輯也是有區別的:ui

private Headers parseHeaders(String[] headers) {
      Headers.Builder builder = new Headers.Builder();
      for (String header : headers) {
        int colon = header.indexOf(':');
        if (colon == -1 || colon == 0 || colon == header.length() - 1) {
          throw methodError(method,
              "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
        }
        String headerName = header.substring(0, colon);
        String headerValue = header.substring(colon + 1).trim();
        if ("Content-Type".equalsIgnoreCase(headerName)) {
          try {
            contentType = MediaType.get(headerValue);
          } catch (IllegalArgumentException e) {
            throw methodError(method, e, "Malformed content type: %s", headerValue);
          }
        } else {
          builder.add(headerName, headerValue);
        }
      }
      return builder.build();
    }

而後繼續看build方法,通過了方法註解處理和一堆的驗證,而後到了參數處理部分:this

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
	parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}

原來for循環還能這麼寫啊,老外就是會玩,parseParameter()的三個參數重的最後一個參數表示循環是否繼續,納尼,我沒看到break、continue和return啊老弟,你怎麼作到的?原來是在parseParameter()方法中去判斷這個參數,若是爲false就直接throw了一個Exception!特麼靠異常去終結循環啊,神來之筆!url

其中的parseParameter方法,就是對各類參數註解的處理。code

相關文章
相關標籤/搜索