Retrofit 2如何处理retrofit.create()传入接口的函数注解?
Retrofit 注解处理核心原理与代码解析
你定义的GiphyApiService接口:
@GET("gifs/search") fun getSearchedGifs( @Query("q") searchTerm: String, @Query("limit") limit: Int = 50, @Query("offset") offset: Int = 0, @Query("rating") rating: String = "g", @Query("lang") lang: String = "en", @Query("bundle") bundle: String = "messaging_non_clips", @Query("api_key") apiKey: String = "my api key" ): Call<DataResult>
创建Retrofit服务实例的代码:
val retroService = retrofit.create(GiphyApiService::class.java)
Retrofit 处理接口注解的核心依赖动态代理和反射解析,retrofit.create() 是整个流程的入口,下面拆解关键逻辑并展示核心代码片段:
一、retrofit.create() 的核心逻辑
调用create方法时,Retrofit会通过动态代理生成接口的实现类,所有接口方法的调用都会被代理类拦截,进而触发注解解析和请求构建流程。
核心简化代码(来自Retrofit类):
public <T> T create(final Class<T> service) { // 校验接口合法性(必须是接口、不能有多重继承等) validateServiceInterface(service); // JDK动态代理生成实现类 return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // 处理Object类的默认方法(如toString、hashCode) if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } // 处理Kotlin/Java 8的接口默认方法 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } // 核心逻辑:解析方法注解,构建可执行的请求处理对象 return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
二、注解解析的核心:ServiceMethod 构建
loadServiceMethod(method) 会把接口方法上的所有注解(@GET、@Query等)解析成可执行的请求逻辑,核心分为两步:解析HTTP方法与路径、解析参数注解。
1. 解析HTTP方法注解(如@GET)
核心代码来自RequestFactory类(负责构建请求的元数据):
private void parseMethodAnnotation(Annotation annotation) { if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } // 同理处理PUT、DELETE、PATCH等其他HTTP方法注解 } private void parseHttpMethodAndPath(String httpMethod, String path, boolean hasBody) { this.httpMethod = httpMethod; // 记录请求方法:GET this.relativeUrl = path; // 记录请求路径:gifs/search this.hasBody = hasBody; // GET请求无请求体,标记为false }
2. 解析参数注解(如@Query)
对于@Query("q") searchTerm: String这类参数,Retrofit会解析注解的名称和参数值,最终拼接成URL的查询参数。
核心代码片段:
private ParameterHandler<?> parseParameterAnnotation(Annotation[] annotations, int paramIndex) { ParameterHandler<?> handler = null; for (Annotation annotation : annotations) { handler = parseSingleParameterAnnotation(annotation, paramIndex); if (handler != null) break; } return handler; } private ParameterHandler<?> parseSingleParameterAnnotation(Annotation annotation, int paramIndex) { if (annotation instanceof Query) { Query query = (Query) annotation; String paramName = query.value(); // 获取参数名:q boolean encoded = query.encoded(); // 是否需要编码参数值 // 返回Query参数处理器,后续会把参数值拼接进URL return new ParameterHandler.Query<>(paramName, encoded); } // 同理处理@Path、@Body、@Header等其他参数注解 }
三、你的接口方法调用流程
当你调用retroService.getSearchedGifs("cat")时:
- 动态代理拦截方法调用,传入
getSearchedGifs方法对象和参数列表["cat", 50, 0, "g", "en", "messaging_non_clips", "my api key"] loadServiceMethod解析方法上的@GET("gifs/search"),确定请求方法为GET,路径为gifs/search- 逐个解析每个参数的
@Query注解,将参数名和对应值拼接成URL查询参数,最终生成完整请求URL:https://api.giphy.com/v1/gifs/search?q=cat&limit=50&offset=0&rating=g&lang=en&bundle=messaging_non_clips&api_key=my api key - 构建
Call<DataResult>对象,调用enqueue或execute时发起实际HTTP请求
关键总结
- Retrofit通过动态代理让接口方法的调用转向内部逻辑,无需手动实现接口
- 所有注解的解析都在
ServiceMethod/RequestFactory中完成,将注解信息转化为HTTP请求的具体参数 - 不同的参数注解对应不同的
ParameterHandler,负责将参数值注入到请求的对应位置(URL路径、查询参数、请求体等)
内容的提问来源于stack exchange,提问作者1Sayd
相关产品推荐
相关产品推荐

