一、引言
在現代 Android 和 Java 開發中,網絡請求是不可或缺的一部分。Retrofit 作為 Square 公司開源的一款強大的類型安全的 HTTP 客戶端,憑借其簡潔易用的 API 和高效的性能,在開發者社區中廣受歡迎。Retrofit 的核心特性之一便是通過注解來定義 HTTP 請求,這種方式使得代碼更加清晰、易讀且易于維護。本文將深入 Retrofit 框架的源碼,對其注解定義與解析模塊進行全面且細致的分析,揭示其背后的實現原理。
二、Retrofit 框架概述
2.1 Retrofit 的基本工作流程
Retrofit 的主要工作流程可以概括為以下幾個步驟:
- 定義服務接口:開發者使用注解定義一個接口,該接口包含了各種 HTTP 請求方法。
- 創建 Retrofit 實例:通過
Retrofit.Builder
構建 Retrofit 實例,配置請求的基礎 URL、轉換器工廠、調用適配器工廠等。 - 創建服務代理對象:使用 Retrofit 實例創建服務接口的代理對象。
- 發起請求:調用服務代理對象的方法發起 HTTP 請求,并處理響應結果。
2.2 注解在 Retrofit 中的作用
注解在 Retrofit 中扮演著至關重要的角色,它們用于描述 HTTP 請求的各個方面,包括請求方法(如 GET、POST 等)、請求路徑、請求參數、請求頭、請求體等。通過注解,開發者可以以一種聲明式的方式定義請求,而無需編寫繁瑣的網絡請求代碼。
三、Retrofit 注解的定義
3.1 HTTP 請求方法注解
3.1.1 @GET
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 表示 HTTP GET 請求的注解*/
@Documented
// 該注解只能應用于方法上
@Target(ElementType.METHOD)
// 該注解在運行時保留,以便通過反射獲取
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {/*** 指定請求的相對路徑* @return 請求的相對路徑,默認為空字符串*/String value() default "";
}
@GET
注解用于標記一個方法為 HTTP GET 請求,value
屬性用于指定請求的相對路徑。例如:
java
public interface ApiService {@GET("users/{id}")Call<User> getUser(@Path("id") int userId);
}
3.1.2 其他 HTTP 請求方法注解
除了 @GET
注解,Retrofit 還提供了 @POST
、@PUT
、@DELETE
、@HEAD
、@OPTIONS
和 @PATCH
等注解,它們的定義方式與 @GET
注解類似,只是用于不同的 HTTP 請求方法。
3.2 請求路徑與參數注解
3.2.1 @Path
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于替換 URL 中占位符的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Path {/*** 指定 URL 中的占位符名稱* @return 占位符名稱*/String value();/*** 指定是否對參數進行 URL 編碼,默認為 false* @return 是否進行 URL 編碼*/boolean encoded() default false;
}
@Path
注解用于替換 URL 中的占位符,例如在上面的 getUser
方法中,@Path("id")
表示將 userId
參數的值替換到 URL 中的 {id}
占位符處。
3.2.2 @Query
和 @QueryMap
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加查詢參數到 URL 中的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {/*** 指定查詢參數的名稱* @return 查詢參數的名稱*/String value();/*** 指定是否對參數進行 URL 編碼,默認為 false* @return 是否進行 URL 編碼*/boolean encoded() default false;
}import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加多個查詢參數到 URL 中的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryMap {/*** 指定是否對參數進行 URL 編碼,默認為 false* @return 是否進行 URL 編碼*/boolean encoded() default false;
}
@Query
注解用于添加單個查詢參數到 URL 中,@QueryMap
注解用于添加多個查詢參數。例如:
java
public interface ApiService {@GET("search")Call<List<Item>> search(@Query("keyword") String keyword, @Query("page") int page);@GET("search")Call<List<Item>> searchWithMap(@QueryMap Map<String, String> queryParams);
}
3.2.3 @Header
和 @HeaderMap
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加請求頭的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Header {/*** 指定請求頭的名稱* @return 請求頭的名稱*/String value();
}import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加多個請求頭的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface HeaderMap {
}
@Header
注解用于添加單個請求頭,@HeaderMap
注解用于添加多個請求頭。例如:
java
public interface ApiService {@GET("data")Call<Data> getData(@Header("Authorization") String token);@GET("data")Call<Data> getDataWithMap(@HeaderMap Map<String, String> headers);
}
3.2.4 @Body
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于指定請求體的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Body {
}
@Body
注解用于指定請求體,通常用于 POST、PUT 等請求。例如:
java
public interface ApiService {@POST("users")Call<User> createUser(@Body User user);
}
3.2.5 @FormUrlEncoded
、@Field
和 @FieldMap
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于標記表單編碼請求的注解*/
@Documented
// 該注解只能應用于方法上
@Target(ElementType.METHOD)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface FormUrlEncoded {
}import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加表單字段的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {/*** 指定表單字段的名稱* @return 表單字段的名稱*/String value();/*** 指定是否對參數進行 URL 編碼,默認為 false* @return 是否進行 URL 編碼*/boolean encoded() default false;
}import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加多個表單字段的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldMap {/*** 指定是否對參數進行 URL 編碼,默認為 false* @return 是否進行 URL 編碼*/boolean encoded() default false;
}
@FormUrlEncoded
注解用于標記一個方法為表單編碼請求,@Field
注解用于添加單個表單字段,@FieldMap
注解用于添加多個表單字段。例如:
java
public interface ApiService {@FormUrlEncoded@POST("login")Call<LoginResponse> login(@Field("username") String username, @Field("password") String password);@FormUrlEncoded@POST("login")Call<LoginResponse> loginWithMap(@FieldMap Map<String, String> fields);
}
3.2.6 @Multipart
、@Part
和 @PartMap
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于標記多部分請求的注解*/
@Documented
// 該注解只能應用于方法上
@Target(ElementType.METHOD)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Multipart {
}import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加多部分請求中的一個部分的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Part {/*** 指定部分的名稱* @return 部分的名稱*/String value() default "";
}import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加多部分請求中的多個部分的注解*/
@Documented
// 該注解只能應用于方法的參數上
@Target(ElementType.PARAMETER)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface PartMap {
}
@Multipart
注解用于標記一個方法為多部分請求,@Part
注解用于添加多部分請求中的一個部分,@PartMap
注解用于添加多部分請求中的多個部分。多部分請求常用于文件上傳等場景。例如:
java
public interface ApiService {@Multipart@POST("upload")Call<UploadResponse> uploadFile(@Part("file"; filename="image.jpg"") RequestBody file);@Multipart@POST("upload")Call<UploadResponse> uploadFiles(@PartMap Map<String, RequestBody> files);
}
3.3 其他注解
3.3.1 @Headers
注解
java
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 用于添加多個請求頭的注解*/
@Documented
// 該注解只能應用于方法上
@Target(ElementType.METHOD)
// 該注解在運行時保留
@Retention(RetentionPolicy.RUNTIME)
public @interface Headers {/*** 指定請求頭的數組* @return 請求頭的數組*/String[] value();
}
@Headers
注解用于在方法上添加多個請求頭。例如:
java
public interface ApiService {@Headers({"Content-Type: application/json","Authorization: Bearer token123"})@GET("data")Call<Data> getData();
}
四、Retrofit 注解的解析
4.1 解析入口:ServiceMethod
類
ServiceMethod
是 Retrofit 中解析注解的核心類,它負責將接口方法上的注解信息解析為實際的 HTTP 請求信息。以下是 ServiceMethod
類的部分源碼:
java
abstract class ServiceMethod<T> {/*** 解析接口方法上的注解,創建 ServiceMethod 實例* @param retrofit Retrofit 實例* @param method 接口方法* @param <T> 響應類型* @return ServiceMethod 實例*/static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {// 創建 RequestFactory.Builder 實例,用于構建 RequestFactoryRequestFactory.Builder requestFactoryBuilder = new RequestFactory.Builder(retrofit, method);// 解析請求方法和路徑相關的注解requestFactoryBuilder.parseMethodAnnotation(method.getAnnotations());// 獲取方法的參數類型Type[] parameterTypes = method.getGenericParameterTypes();// 獲取方法的參數注解Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();// 遍歷方法的每個參數for (int p = 0; p < parameterTypes.length; p++) {// 解析參數的注解requestFactoryBuilder.parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);}// 構建 RequestFactory 實例RequestFactory requestFactory = requestFactoryBuilder.build();// 獲取方法的返回類型Type returnType = method.getGenericReturnType();if (Utils.hasUnresolvableType(returnType)) {throw methodError(method,"Method return type must not include a type variable or wildcard: %s", returnType);}if (returnType == void.class) {throw methodError(method, "Service methods cannot return void.");}// 創建 CallAdapter 實例,用于將 Call 對象轉換為其他類型CallAdapter<T, ?> callAdapter = createCallAdapter(retrofit, method, returnType, requestFactory);// 獲取響應類型Type responseType = callAdapter.responseType();if (Utils.hasUnresolvableType(responseType)) {throw methodError(method, "Call return type must not include a type variable or wildcard: %s", returnType);}// 創建 Converter 實例,用于將響應數據轉換為指定類型Converter<ResponseBody, T> responseConverter =createResponseConverter(retrofit, method, responseType);// 獲取 OkHttpClient 實例okhttp3.Call.Factory callFactory = retrofit.callFactory();// 創建 ServiceMethod 實例return new HttpServiceMethod<>(callFactory, requestFactory, callAdapter, responseConverter);}/*** 創建 CallAdapter 實例* @param retrofit Retrofit 實例* @param method 接口方法* @param returnType 方法的返回類型* @param requestFactory 請求工廠* @param <T> 響應類型* @return CallAdapter 實例*/private static <T> CallAdapter<T, ?> createCallAdapter(Retrofit retrofit, Method method, Type returnType, RequestFactory requestFactory) {try {// 通過 Retrofit 實例獲取 CallAdapter.Factory 列表,并調用其 get 方法創建 CallAdapter 實例return (CallAdapter<T, ?>) retrofit.callAdapter(returnType, method.getAnnotations());} catch (RuntimeException e) {throw methodError(method, e, "Unable to create call adapter for %s", returnType);}}/*** 創建響應轉換器實例* @param retrofit Retrofit 實例* @param method 接口方法* @param responseType 響應類型* @param <T> 響應類型* @return 響應轉換器實例*/private static <T> Converter<ResponseBody, T> createResponseConverter(Retrofit retrofit, Method method, Type responseType) {try {// 通過 Retrofit 實例獲取 Converter.Factory 列表,并調用其 responseBodyConverter 方法創建響應轉換器實例return retrofit.responseBodyConverter(responseType, method.getAnnotations());} catch (RuntimeException e) {throw methodError(method, e, "Unable to create converter for %s", responseType);}}/*** 抽象方法,用于執行請求* @param args 方法參數* @return 響應結果*/abstract @Nullable T invoke(Object[] args);
}
parseAnnotations
方法是解析的入口,它接收 Retrofit
實例和 Method
實例作為參數,通過以下步驟完成注解解析:
- 創建
RequestFactory.Builder
實例,用于構建RequestFactory
。 - 調用
requestFactoryBuilder.parseMethodAnnotation
方法解析請求方法和路徑相關的注解。 - 遍歷方法的每個參數,調用
requestFactoryBuilder.parseParameter
方法解析參數的注解。 - 構建
RequestFactory
實例。 - 獲取方法的返回類型,并創建
CallAdapter
實例,用于將Call
對象轉換為其他類型。 - 獲取響應類型,并創建
Converter
實例,用于將響應數據轉換為指定類型。 - 獲取
OkHttpClient
實例,并創建ServiceMethod
實例。
4.2 解析請求方法和路徑注解:RequestFactory.Builder.parseMethodAnnotation
方法
java
static final class Builder {// 存儲請求方法(如 GET、POST 等)private String httpMethod;// 存儲請求是否需要請求體private boolean hasBody;// 存儲請求的相對路徑private String relativeUrl;// 存儲請求的請求頭private okhttp3.Headers headers;// 存儲路徑中的占位符名稱private List<String> relativeUrlParamNames;// 標記是否為多部分請求private boolean isMultipart;// 標記是否為表單編碼請求private boolean isFormEncoded;/*** 解析請求方法和路徑相關的注解* @param annotations 方法上的注解數組*/void parseMethodAnnotation(Annotation[] annotations) {for (Annotation annotation : annotations) {if (annotation instanceof HttpMethod) {// 如果注解是 HttpMethod 類型(如 @GET、@POST 等)HttpMethod httpMethodAnnotation = (HttpMethod) annotation;// 獲取請求方法this.httpMethod = httpMethodAnnotation.value();// 判斷是否需要請求體this.hasBody = httpMethodAnnotation.hasBody();String path = httpMethodAnnotation.path();if (!path.isEmpty()) {// 檢查路徑是否以 / 開頭if (path.startsWith("/")) {throw methodError(method, "@%s path must not start with /: %s",httpMethodAnnotation.annotationType().getSimpleName(), path);}this.relativeUrl = path;// 解析路徑中的占位符this.relativeUrlParamNames = parsePathParameters(path);}} else if (annotation instanceof Headers) {// 如果注解是 Headers 類型String[] headersToParse = ((Headers) annotation).value();if (headersToParse.length == 0) {throw methodError(method, "@Headers annotation is empty.");}// 解析請求頭this.headers = parseHeaders(headersToParse);} else if (annotation instanceof Multipart) {// 如果注解是 Multipart 類型if (this.hasBody) {throw methodError(method, "Only one encoding annotation is allowed.");}this.isMultipart = true;this.hasBody = true;} else if (annotation instanceof FormUrlEncoded) {// 如果注解是 FormUrlEncoded 類型if (this.hasBody) {throw methodError(method, "Only one encoding annotation is allowed.");}this.isFormEncoded = true;this.hasBody = true;}}if (httpMethod == null) {throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");}}/*** 解析路徑中的占位符* @param path 請求路徑* @return 占位符名稱列表*/private static List<String> parsePathParameters(String path) {// 定義正則表達式,用于匹配路徑中的占位符Matcher m = PARAM_URL_REGEX.matcher(path);List<String> patterns = new ArrayList<>();while (m.find()) {String name = m.group(1);if (name == null) {continue;}if (patterns.contains(name)) {throw new IllegalArgumentException("URL path "" + path + "" has duplicate param "" + name + "".");}patterns.add(name);}return patterns;}/*** 解析請求頭* @param headers 請求頭數組* @return OkHttp 的 Headers 實例*/private static okhttp3.Headers parseHeaders(String[] headers) {okhttp3.Headers.Builder builder = new okhttp3.Headers.Builder();for (String header : headers) {int colon = header.indexOf(':');if (colon == -1 || colon == 0 || colon == header.length() - 1) {throw new IllegalArgumentException("Headers value must be in the form "Name: Value". Found: "" + header + """);}String name = header.substring(0, colon).trim();String value = header.substring(colon + 1).trim();builder.add(name, value);}return builder.build();}
}
parseMethodAnnotation
方法遍歷方法上的所有注解,根據注解的類型進行不同的處理:
- 如果注解是
HttpMethod
類型(如@GET
、@POST
等),獲取請求方法、判斷是否需要請求體,并解析路徑中的占位符。 - 如果注解是
Headers
類型,解析請求頭。 - 如果注解是
Multipart
類型,標記為多部分請求。 - 如果注解是
FormUrlEncoded
類型,標記為表單編碼請求。
4.3 解析參數注解:RequestFactory.Builder.parseParameter
方法
java
static final class Builder {// 存儲參數處理器數組private ParameterHandler<?>[] parameterHandlers;/*** 解析方法參數的注解* @param p 參數索引* @param parameterType 參數類型* @param annotations 參數上的注解數組*/void parseParameter(int p, Type parameterType, @Nullable Annotation[] annotations) {if (annotations == null) {throw parameterError(method, p, "No Retrofit annotation found.");}ParameterHandler<?> result = null;for (Annotation annotation : annotations) {// 創建參數處理器ParameterHandler<?> annotationAction = parseParameterAnnotation(p, parameterType, annotations, annotation);if (annotationAction == null) {continue;}if (result != null) {throw parameterError(method, p, "Multiple Retrofit annotations found, only one allowed.");}result = annotationAction;}if (result == null) {throw parameterError(method, p, "No Retrofit annotation found.");}// 將參數處理器添加到參數處理器數組中parameterHandlers[p] = result;}/*** 解析單個參數注解* @param p 參數索引* @param type 參數類型* @param annotations 參數上的注解數組* @param annotation 當前要解析的注解* @return 參數處理器*/private @Nullable ParameterHandler<?> parseParameterAnnotation(int p, Type type, Annotation[] annotations, Annotation annotation) {if (annotation instanceof Path) {// 如果注解
4.3 解析參數注解:RequestFactory.Builder.parseParameter
方法(續)
java
static final class Builder {// 存儲參數處理器數組private ParameterHandler<?>[] parameterHandlers;/*** 解析方法參數的注解* @param p 參數索引* @param parameterType 參數類型* @param annotations 參數上的注解數組*/void parseParameter(int p, Type parameterType, @Nullable Annotation[] annotations) {if (annotations == null) {throw parameterError(method, p, "No Retrofit annotation found.");}ParameterHandler<?> result = null;for (Annotation annotation : annotations) {// 創建參數處理器ParameterHandler<?> annotationAction = parseParameterAnnotation(p, parameterType, annotations, annotation);if (annotationAction == null) {continue;}if (result != null) {throw parameterError(method, p, "Multiple Retrofit annotations found, only one allowed.");}result = annotationAction;}if (result == null) {throw parameterError(method, p, "No Retrofit annotation found.");}// 將參數處理器添加到參數處理器數組中parameterHandlers[p] = result;}/*** 解析單個參數注解* @param p 參數索引* @param type 參數類型* @param annotations 參數上的注解數組* @param annotation 當前要解析的注解* @return 參數處理器*/private @Nullable ParameterHandler<?> parseParameterAnnotation(int p, Type type, Annotation[] annotations, Annotation annotation) {if (annotation instanceof Path) {// 如果注解是 Path 類型Path path = (Path) annotation;// 檢查路徑中是否包含該占位符if (!relativeUrlParamNames.contains(path.value())) {throw parameterError(method, p, "@Path parameter "" + path.value()+ "" not found in relative URL "" + relativeUrl + """);}// 檢查參數類型是否為基本類型或字符串if (Utils.hasUnresolvableType(type)) {throw parameterError(method, p,"@Path parameter type must not include a type variable or wildcard: %s", type);}// 創建 PathParameterHandler 實例return new ParameterHandler.Path<>(path.value(), path.encoded(),converterFactory.stringConverter(type, annotations, retrofit));} else if (annotation instanceof Query) {// 如果注解是 Query 類型Query query = (Query) annotation;// 檢查參數類型是否為基本類型或字符串if (Utils.hasUnresolvableType(type)) {throw parameterError(method, p,"@Query parameter type must not include a type variable or wildcard: %s", type);}// 創建 QueryParameterHandler 實例return new ParameterHandler.Query<>(query.value(), query.encoded(),converterFactory.stringConverter(type, annotations, retrofit));} else if (annotation instanceof QueryMap) {// 如果注解是 QueryMap 類型if (!Map.class.isAssignableFrom(Utils.getRawType(type))) {throw parameterError(method, p, "@QueryMap parameter type must be Map.");}Type keyType = Utils.getSupertype(type, Map.class, String.class);if (keyType != String.class) {throw parameterError(method, p, "@QueryMap keys must be of type String: %s", type);}Type valueType = Utils.getSupertype(type, Map.class, Object.class);// 創建 QueryMapParameterHandler 實例return new ParameterHandler.QueryMap<>(converterFactory.stringConverter(valueType, annotations, retrofit));} else if (annotation instanceof Header) {// 如果注解是 Header 類型Header header = (Header) annotation;// 檢查參數類型是否為基本類型或字符串if (Utils.hasUnresolvableType(type)) {throw parameterError(method, p,"@Header parameter type must not include a type variable or wildcard: %s", type);}// 創建 HeaderParameterHandler 實例return new ParameterHandler.Header<>(header.value(),converterFactory.stringConverter(type, annotations, retrofit));} else if (annotation instanceof HeaderMap) {// 如果注解是 HeaderMap 類型if (!Map.class.isAssignableFrom(Utils.getRawType(type))) {throw parameterError(method, p, "@HeaderMap parameter type must be Map.");}Type keyType = Utils.getSupertype(type, Map.class, String.class);if (keyType != String.class) {throw parameterError(method, p, "@HeaderMap keys must be of type String: %s", type);}Type valueType = Utils.getSupertype(type, Map.class, Object.class);// 創建 HeaderMapParameterHandler 實例return new ParameterHandler.HeaderMap<>(converterFactory.stringConverter(valueType, annotations, retrofit));} else if (annotation instanceof Body) {// 如果注解是 Body 類型if (isFormEncoded || isMultipart) {throw parameterError(method, p,"@Body parameters cannot be used with form or multi - part encoding.");}// 創建 BodyParameterHandler 實例return new ParameterHandler.Body<>(converterFactory.requestBodyConverter(type, annotations, methodAnnotations, retrofit));} else if (annotation instanceof Field) {// 如果注解是 Field 類型if (!isFormEncoded) {throw parameterError(method, p, "@Field parameters can only be used with form encoding.");}Field field = (Field) annotation;// 檢查參數類型是否為基本類型或字符串if (Utils.hasUnresolvableType(type)) {throw parameterError(method, p,"@Field parameter type must not include a type variable or wildcard: %s", type);}// 創建 FieldParameterHandler 實例return new ParameterHandler.Field<>(field.value(), field.encoded(),converterFactory.stringConverter(type, annotations, retrofit));} else if (annotation instanceof FieldMap) {// 如果注解是 FieldMap 類型if (!isFormEncoded) {throw parameterError(method, p, "@FieldMap parameters can only be used with form encoding.");}if (!Map.class.isAssignableFrom(Utils.getRawType(type))) {throw parameterError(method, p, "@FieldMap parameter type must be Map.");}Type keyType = Utils.getSupertype(type, Map.class, String.class);if (keyType != String.class) {throw parameterError(method, p, "@FieldMap keys must be of type String: %s", type);}Type valueType = Utils.getSupertype(type, Map.class, Object.class);// 創建 FieldMapParameterHandler 實例return new ParameterHandler.FieldMap<>(converterFactory.stringConverter(valueType, annotations, retrofit));} else if (annotation instanceof Part) {// 如果注解是 Part 類型if (!isMultipart) {throw parameterError(method, p, "@Part parameters can only be used with multipart encoding.");}Part part = (Part) annotation;String partName = part.value();if (!"".equals(partName) && !partName.endsWith(";")) {partName = partName + ";";}// 創建 PartParameterHandler 實例return new ParameterHandler.Part<>(partName,converterFactory.requestBodyConverter(type, annotations, methodAnnotations, retrofit));} else if (annotation instanceof PartMap) {// 如果注解是 PartMap 類型if (!isMultipart) {throw parameterError(method, p, "@PartMap parameters can only be used with multipart encoding.");}if (!Map.class.isAssignableFrom(Utils.getRawType(type))) {throw parameterError(method, p, "@PartMap parameter type must be Map.");}Type keyType = Utils.getSupertype(type, Map.class, String.class);if (keyType != String.class) {throw parameterError(method, p, "@PartMap keys must be of type String: %s", type);}Type valueType = Utils.getSupertype(type, Map.class, Object.class);// 創建 PartMapParameterHandler 實例return new ParameterHandler.PartMap<>(converterFactory.requestBodyConverter(valueType, annotations, methodAnnotations, retrofit));}return null;}
}
4.3.1 詳細解析邏輯
-
@Path
注解解析:- 首先,從
Path
注解中獲取占位符名稱,檢查該占位符是否存在于之前解析得到的relativeUrlParamNames
列表中。如果不存在,會拋出異常,確保路徑占位符的正確性。 - 接著,檢查參數類型是否包含不可解析的類型變量或通配符。如果存在,也會拋出異常,因為
@Path
參數類型應該是基本類型或字符串。 - 最后,創建
ParameterHandler.Path
實例,該實例負責處理路徑占位符的替換。converterFactory.stringConverter
方法用于創建一個將參數類型轉換為字符串的轉換器,以便將參數值正確地替換到路徑中。
- 首先,從
-
@Query
注解解析:- 對于
Query
注解,同樣會檢查參數類型是否包含不可解析的類型變量或通配符。 - 然后創建
ParameterHandler.Query
實例,該實例會將參數值作為查詢參數添加到 URL 中。converterFactory.stringConverter
用于將參數值轉換為字符串形式。
- 對于
-
@QueryMap
注解解析:- 先檢查參數類型是否為
Map
類型,如果不是會拋出異常。 - 接著檢查
Map
的鍵類型是否為String
類型,若不是也會拋出異常。 - 最后創建
ParameterHandler.QueryMap
實例,該實例會將Map
中的鍵值對作為查詢參數添加到 URL 中。converterFactory.stringConverter
用于將Map
的值轉換為字符串。
- 先檢查參數類型是否為
-
@Header
注解解析:- 檢查參數類型是否包含不可解析的類型變量或通配符。
- 創建
ParameterHandler.Header
實例,該實例會將參數值作為請求頭添加到請求中。converterFactory.stringConverter
用于將參數值轉換為字符串形式的請求頭值。
-
@HeaderMap
注解解析:- 檢查參數類型是否為
Map
類型,以及Map
的鍵類型是否為String
類型。 - 創建
ParameterHandler.HeaderMap
實例,該實例會將Map
中的鍵值對作為請求頭添加到請求中。converterFactory.stringConverter
用于將Map
的值轉換為字符串形式的請求頭值。
- 檢查參數類型是否為
-
@Body
注解解析:- 檢查當前請求是否為表單編碼或多部分請求,如果是則拋出異常,因為
@Body
參數不能與表單或多部分編碼同時使用。 - 創建
ParameterHandler.Body
實例,該實例會將參數作為請求體發送。converterFactory.requestBodyConverter
用于將參數類型轉換為RequestBody
類型,以便進行網絡傳輸。
- 檢查當前請求是否為表單編碼或多部分請求,如果是則拋出異常,因為
-
@Field
注解解析:- 檢查當前請求是否為表單編碼請求,如果不是則拋出異常,因為
@Field
參數只能用于表單編碼請求。 - 檢查參數類型是否包含不可解析的類型變量或通配符。
- 創建
ParameterHandler.Field
實例,該實例會將參數值作為表單字段添加到請求體中。converterFactory.stringConverter
用于將參數值轉換為字符串形式的表單字段值。
- 檢查當前請求是否為表單編碼請求,如果不是則拋出異常,因為
-
@FieldMap
注解解析:- 檢查當前請求是否為表單編碼請求,以及參數類型是否為
Map
類型,Map
的鍵類型是否為String
類型。 - 創建
ParameterHandler.FieldMap
實例,該實例會將Map
中的鍵值對作為表單字段添加到請求體中。converterFactory.stringConverter
用于將Map
的值轉換為字符串形式的表單字段值。
- 檢查當前請求是否為表單編碼請求,以及參數類型是否為
-
@Part
注解解析:- 檢查當前請求是否為多部分請求,如果不是則拋出異常,因為
@Part
參數只能用于多部分請求。 - 處理
Part
注解的value
屬性,確保其格式正確。 - 創建
ParameterHandler.Part
實例,該實例會將參數作為多部分請求的一部分添加到請求體中。converterFactory.requestBodyConverter
用于將參數類型轉換為RequestBody
類型。
- 檢查當前請求是否為多部分請求,如果不是則拋出異常,因為
-
@PartMap
注解解析:- 檢查當前請求是否為多部分請求,以及參數類型是否為
Map
類型,Map
的鍵類型是否為String
類型。 - 創建
ParameterHandler.PartMap
實例,該實例會將Map
中的鍵值對作為多部分請求的多個部分添加到請求體中。converterFactory.requestBodyConverter
用于將Map
的值轉換為RequestBody
類型。
- 檢查當前請求是否為多部分請求,以及參數類型是否為
4.4 創建 RequestFactory
實例
在完成所有參數注解的解析后,會調用 RequestFactory.Builder
的 build
方法來構建 RequestFactory
實例:
java
RequestFactory build() {return new RequestFactory(this);
}
RequestFactory
類封裝了所有解析得到的請求信息,包括請求方法、請求路徑、請求頭、請求體等,后續會根據這些信息創建實際的 Request
對象。
4.5 創建 CallAdapter
和 Converter
實例
在 ServiceMethod.parseAnnotations
方法中,還會創建 CallAdapter
和 Converter
實例:
java
// 創建 CallAdapter 實例,用于將 Call 對象轉換為其他類型
CallAdapter<T, ?> callAdapter = createCallAdapter(retrofit, method, returnType, requestFactory);
// 獲取響應類型
Type responseType = callAdapter.responseType();
if (Utils.hasUnresolvableType(responseType)) {throw methodError(method, "Call return type must not include a type variable or wildcard: %s", returnType);
}// 創建 Converter 實例,用于將響應數據轉換為指定類型
Converter<ResponseBody, T> responseConverter =createResponseConverter(retrofit, method, responseType);
4.5.1 CallAdapter
實例創建
createCallAdapter
方法通過 Retrofit
實例的 callAdapter
方法從 CallAdapter.Factory
列表中查找合適的 CallAdapter.Factory
,并調用其 get
方法創建 CallAdapter
實例。CallAdapter
的作用是將 Call
對象轉換為其他類型,例如將 Call<Response>
轉換為 Observable<Response>
等,以支持不同的異步編程模型。
4.5.2 Converter
實例創建
createResponseConverter
方法通過 Retrofit
實例的 responseBodyConverter
方法從 Converter.Factory
列表中查找合適的 Converter.Factory
,并調用其 responseBodyConverter
方法創建 Converter
實例。Converter
的作用是將 ResponseBody
轉換為指定的響應類型,例如將 JSON 數據轉換為 Java 對象。
4.6 創建 ServiceMethod
實例
最后,根據前面解析得到的信息,創建 ServiceMethod
實例:
java
// 獲取 OkHttpClient 實例
okhttp3.Call.Factory callFactory = retrofit.callFactory();
// 創建 ServiceMethod 實例
return new HttpServiceMethod<>(callFactory, requestFactory, callAdapter, responseConverter);
HttpServiceMethod
是 ServiceMethod
的具體實現類,它負責執行實際的 HTTP 請求,并處理響應結果。在 invoke
方法中,會根據 RequestFactory
創建 Request
對象,使用 OkHttpClient
發送請求,然后通過 CallAdapter
和 Converter
處理響應。
java
final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {private final okhttp3.Call.Factory callFactory;private final RequestFactory requestFactory;private final CallAdapter<ResponseT, ReturnT> callAdapter;private final Converter<ResponseBody, ResponseT> responseConverter;HttpServiceMethod(okhttp3.Call.Factory callFactory, RequestFactory requestFactory,CallAdapter<ResponseT, ReturnT> callAdapter,Converter<ResponseBody, ResponseT> responseConverter) {this.callFactory = callFactory;this.requestFactory = requestFactory;this.callAdapter = callAdapter;this.responseConverter = responseConverter;}@Override@Nullable ReturnT invoke(Object[] args) {// 創建 OkHttp 的 Request 對象Request request = requestFactory.create(args);// 創建 OkHttp 的 Call 對象okhttp3.Call call = callFactory.newCall(request);// 調用 CallAdapter 的 adapt 方法將 Call 對象轉換為指定類型return callAdapter.adapt(new OkHttpCall<>(request, callFactory, responseConverter));}
}
五、注解解析后的使用
當 ServiceMethod
實例創建完成后,就可以使用它來發起 HTTP 請求了。在調用服務接口的方法時,實際上是調用 ServiceMethod
的 invoke
方法:
java
public interface ApiService {@GET("users/{id}")Call<User> getUser(@Path("id") int userId);
}// 創建 Retrofit 實例
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).build();// 創建服務代理對象
ApiService apiService = retrofit.create(ApiService.class);// 發起請求
Call<User> call = apiService.getUser(1);
call.enqueue(new Callback<User>() {@Overridepublic void onResponse(Call<User> call, Response<User> response) {if (response.isSuccessful()) {User user = response.body();// 處理響應數據} else {// 處理請求失敗}}@Overridepublic void onFailure(Call<User> call, Throwable t) {// 處理請求異常}
});
在上述代碼中,apiService.getUser(1)
實際上調用了 ServiceMethod
的 invoke
方法,該方法會根據之前解析得到的注解信息創建 Request
對象,使用 OkHttpClient
發送請求,并通過 CallAdapter
和 Converter
處理響應結果。
六、總結
Retrofit 的注解定義與解析模塊是其核心功能之一,通過使用注解,開發者可以以一種簡潔、聲明式的方式定義 HTTP 請求。在解析過程中,Retrofit 利用 Java 的反射機制,在運行時獲取方法和參數上的注解信息,并根據注解類型進行相應的處理。具體步驟包括解析請求方法和路徑注解、解析參數注解、創建 RequestFactory
、CallAdapter
和 Converter
實例,最終創建 ServiceMethod
實例來執行實際的 HTTP 請求。這種設計使得 Retrofit 具有高度的靈活性和可擴展性,開發者可以通過自定義 CallAdapter.Factory
和 Converter.Factory
來滿足不同的需求。同時,注解的使用也使得代碼更加清晰、易讀和易于維護,提高了開發效率。