重识动态代理

前言

从代理模式出发,逐步探索 Retrofit 的奥秘

代理模式

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

静态代理

关于静态代理本身的实现就很简单了,这里可以简单看一下实现。

java 复制代码
public interface IFooDao {
    void doSomething();
}

public class IFooDaoImpl implements IFooDao {
    @Override
    public void doSomething() {
        System.out.println(this.getClass().getName() + " work");
    }
}

public class IFooDaoProxy implements IFooDao {
    private IFooDao mIFooDao;

    public IFooDaoProxy(IFooDao mIFooDao) {
        this.mIFooDao = mIFooDao;
    }

    @Override
    public void doSomething() {
        this.mIFooDao.doSomething();
    }
}

依次创建了服务接口、服务实现、服务代理

java 复制代码
public class Client {
    public static void main(String[] args) {
        IFooDao prox = new IFooDaoProxy(new IFooDaoImpl());
        prox.doSomething();
    }
}

静态代理本身的实现很简单,其中涉及到的类也会比较明确。这里需要注意的是,服务接口的实现不一定通过定义 接口类的方式实现(即不一定是 interface ),抽象类也是一种选择,比如 Android 中的 Activity 为了实现在不通过版本的兼容性,就是采用了 AppCompatDelegate 这个抽象类。Activity 本身并不负责实现 setContentView() 之类的功能。而是将其代理给 AppCompatDelegate 的实现类 AppCompatDelegateImpl

动态代理

可以看到,在静态代理中,代理类需要提前创建,而通过动态代理,就可以在运行期实现创建代理类的效果。

依次定义接口和注解

java 复制代码
public interface IFooDao {
    void doSomething();

    @FakeProxy("proxy is yyds")
    int add(@EasyProxy String a, @Nullable String b);
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface EasyProxy {
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FakeProxy {
    String value() default "";
}

上面定义了接口和 EasyProxyFakeProxy 两个注解。

java 复制代码
    private static void dynamicProxy(IFooDao realDao) {
        IFooDao proxy = (IFooDao) Proxy.newProxyInstance(
                realDao.getClass().getClassLoader(),
                realDao.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.printf("proxy=%s,method=%s,args=%s\n",
                                proxy.getClass().getSimpleName(), method.getName(), Arrays.toString(args));

                        System.out.println(method.getGenericReturnType());
                        System.out.println(Arrays.toString(method.getAnnotations()));
                        System.out.println(Arrays.toString(method.getGenericParameterTypes()));
                        System.out.println(Arrays.deepToString(method.getParameterAnnotations()));

                        // any-operation-before invoke
                        Object object = method.invoke(realDao, args);
                        // any-operation-after invoke
                        System.out.println("========================");
                        return object;
                    }
                }
        );
        proxy.doSomething();
        proxy.add("1", "2");
    }


public class IFooDaoImpl implements IFooDao {
    @Override
    public void doSomething() {
        System.out.println(this.getClass().getName() + " work");
    }

    @Override
    public int add(@EasyProxy String a, @Nullable String b) {
        return Integer.parseInt(a) + Integer.parseInt(b);
    }
}

// main 
dynamicProxy(new IFooDaoImpl());

可以看到,利用 Java 系统提供的 Proxy 静态方法 newProxyInstance ,只需要提供 IFooDao 的一个具体实现,那么就可以返回一个代理类,通过这个代理类 proxy 就可以访问 IFooDao 中定义的接口了。

可以看一下输出

shell 复制代码
proxy=$Proxy0,method=doSomething,args=null
void
[]
[]
[]
com.dp.internal.proxy.IFooDaoImpl work
========================
proxy=$Proxy0,method=add,args=[1, 2]
int
[@com.dp.internal.proxy.FakeProxy(value=proxy is yyds)]
[class java.lang.String, class java.lang.String]
[[@com.dp.internal.proxy.EasyProxy()], []]
========================

可以看到在 InvocationHandlerinvoke 方法中,我们通过 method 可以获取到关于当前 method 的所有信息,包括其参数、方法注解、参数注解等信息。这里值得注意的是 invoke 方法是有返回值的。

Retrofit

关于动态代理,Android 中使用最有名的莫过于 Retrofit 框架的实现了。下面就来看看 Retrofit 是如何使用动态代理的。

以下分析基于 com.squareup.retrofit2:retrofit:2.9.0

Retrofit 到底做了什么?

kotlin 复制代码
interface ApiService {

    @GET("wxarticle/chapters/json")
    fun getAccountList(): Call<WeChatCountList>

    @GET("wxarticle/chapters/json")
    fun getAccountJson(): Call<ResponseBody>
    
    @GET("wxarticle/chapters/json")
    fun getWeChatAccountList(): Observable<WeChatCountList>
}

  fun go2() {
        val retrofit = Retrofit.Builder()
            .baseUrl(baseUrl)
            .build()
        val service = retrofit.create(ApiService::class.java)

        val call = service.getAccountJson()
    }

这里需要注意的是,ApiService 中定义的前两个方法返回值都是 Call 类型的。getWeChatAccountList 返回的是 Observable 类型。但这对于 Retrofit 整体的实现来说是没有什么大的区别的,无非只是多了一个 CallAdapter ,需要把 Call 执行的结果用 Observeable 进行一次包装。使用 Retrofit 的本质还是为了发起网络请求,对于网络请求来说最重要的是通过 OkHttp 创建 Call 的实现。因此,从这个视角出发再去看 Retrofit 的实现就会发现他其实简单。

Retrofit build

java 复制代码
    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        // 在 Android 上,这个 Executor 就是主线程
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      // 除了在 build 过程中添加的自定义 CallAdapter,还要添加系统默认的 Adapter 。
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      // 内置的 Converter 主要是对返回的 Response 进行再次处理
      converterFactories.add(new BuiltInConverters()); 
      // build 阶段自定义添加的 Converter
      converterFactories.addAll(this.converterFactories);
      // 如果支持 Java,会添加支持 Optional 操作的 Converter 
      converterFactories.addAll(platform.defaultConverterFactories());

      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

Retrofit 最核心的两个接口就是 CallAdapterConverter

CallAdapter
java 复制代码
/**
 * Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are
 * created by {@linkplain Factory a factory} which is {@linkplain
 * Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit} instance.
 */
public interface CallAdapter<R, T> {
  /**
   * Returns the value type that this adapter uses when converting the HTTP response body to a Java
   * object. For example, the response type for {@code Call<Repo>} is {@code Repo}. This type is
   * used to prepare the {@code call} passed to {@code #adapt}.
   *
   * <p>Note: This is typically not the same type as the {@code returnType} provided to this call
   * adapter's factory.
   */
  Type responseType();

  /**
   * Returns an instance of {@code T} which delegates to {@code call}.
   *
   * <p>For example, given an instance for a hypothetical utility, {@code Async}, this instance
   * would return a new {@code Async<R>} which invoked {@code call} when run.
   *
   * <pre><code>
   * &#64;Override
   * public &lt;R&gt; Async&lt;R&gt; adapt(final Call&lt;R&gt; call) {
   *   return Async.create(new Callable&lt;Response&lt;R&gt;&gt;() {
   *     &#64;Override
   *     public Response&lt;R&gt; call() throws Exception {
   *       return call.execute();
   *     }
   *   });
   * }
   * </code></pre>
   */
  T adapt(Call<R> call);

  /**
   * Creates {@link CallAdapter} instances based on the return type of {@linkplain
   * Retrofit#create(Class) the service interface} methods.
   */
  abstract class Factory {
    /**
     * Returns a call adapter for interface methods that return {@code returnType}, or null if it
     * cannot be handled by this factory.
     */
    public abstract @Nullable CallAdapter<?, ?> get(
        Type returnType, Annotation[] annotations, Retrofit retrofit);

    /**
     * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
     * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
     */
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    /**
     * Extract the raw class type from {@code type}. For example, the type representing {@code
     * List<? extends Runnable>} returns {@code List.class}.
     */
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}
Converter
java 复制代码
/**
 * Convert objects to and from their representation in HTTP. Instances are created by {@linkplain
 * Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed}
 * into the {@link Retrofit} instance.
 */
public interface Converter<F, T> {
  @Nullable
  T convert(F value) throws IOException;

  /** Creates {@link Converter} instances based on a type and target usage. */
  abstract class Factory {
    /**
     * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if
     * {@code type} cannot be handled by this factory. This is used to create converters for
     * response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}
     * declaration.
     */
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    /**
     * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if
     * {@code type} cannot be handled by this factory. This is used to create converters for types
     * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap} values.
     */
    public @Nullable Converter<?, RequestBody> requestBodyConverter(
        Type type,
        Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations,
        Retrofit retrofit) {
      return null;
    }

    /**
     * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if
     * {@code type} cannot be handled by this factory. This is used to create converters for types
     * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values, {@link Header @Header},
     * {@link HeaderMap @HeaderMap}, {@link Path @Path}, {@link Query @Query}, and {@link
     * QueryMap @QueryMap} values.
     */
    public @Nullable Converter<?, String> stringConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    /**
     * Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
     * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
     */
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }

    /**
     * Extract the raw class type from {@code type}. For example, the type representing {@code
     * List<? extends Runnable>} returns {@code List.class}.
     */
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}
  • CallAdapter 顾名思义,就是把 OkHttp 的 Call 通过适配器转换成任意我们在接口方法中声明的返回类型,比如 Observeable .如果没有添加 自定义的 CallAdapter ,Retrofit 默认返回的就是 Call 类型的 Call. 那么其中的 T 又是怎么决定的呢?这就要靠 Converter 了。OkHttp 默认返回的是 Call<ResponseBody>
  • Converter 就是对返回的 ResponseBody 进行类型转换,比如我们非常熟悉的 addConverterFactory(GsonConverterFactory.create()) .这个转换器具体是用什么库实现 json 到 object 的转换,对于 Retrofit 来说并不重要。

对于 Retrofit 来说,上述两个转换器内部的实现完全是透明的,Retrofit 真是按责任链模式,将数据一层层的传递给 callAdapterFactoriesconverterFactories 中剩余的转换器,直到全部消费完成。

那么这个过程具体是怎么展开的呢?下面就从 Retrofit 的创建展开分析。

Retrofit create 到底创建了啥?

java 复制代码
  public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    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 @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

create 方法总结起来很简单,就是创建了传入的 service 这个类的一个动态代理。这里就是有意思的地方了,我们知道在 Java 中 interface 类型的类是无法创建实例的,比如 ApiService 这个接口,我们可以通过创建匿名类的方式实现这个接口,但是那样需要在实现接口的地方实现其所有方法的及其细节,这显然不是我们想要的。

而 Retrofit 通过动态代理,相当于是动态持有了接口实现的引用,而当我们单独调用接口中的每一个方法时,便会触发其 invoke 方法,在这个方法中通过 method 参数可以获取到当前调用方法的所有信息,通过这些信息就可以实现方法的具体细节了。可以说 Retrofit 的设计非常巧妙,利用接口完全解耦了定义和实现。

Retrofit 实现细节

kotlin 复制代码
  fun go2() {
        val retrofit = Retrofit.Builder()
            .baseUrl(baseUrl)
            .build()
        val service = retrofit.create(ApiService::class.java)
        println("service is $service")
        println("service is ${service.javaClass.name}")
        val call = service.getAccountJson()
    }

我们通过 create 创建了 ApiService 的实例。那么 service 到底是什么呢?我们可以看一下

shell 复制代码
service is retrofit2.Retrofit$1@14d3bc22
service is com.sun.proxy.$Proxy0

可以看到这个实例的 Class 类型是 com.sun.proxy.$Proxy0 。这个类型其实就是 InvocationHandler 中 invoke 方法的第一个参数 Object 的类型。

再看调用链,当我们调用 service.getAccountJson() 方法时

可以看到通过动态代理会调用到 invoke 方法中,最终会调用 loadServiceMethod(method).invoke(args) 方法。

Retrofit 只有默认 CallAdapter 和 Converter 时的执行流程。

可以说 Retrofit 的整体设计非常的巧妙。

参考文档

相关推荐
F-2H几秒前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
苹果酱05673 分钟前
「Mysql优化大师一」mysql服务性能剖析工具
java·vue.js·spring boot·mysql·课程设计
_oP_i1 小时前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx1 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康1 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘2 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意3 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上3 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人4 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言