重识动态代理

前言

从代理模式出发,逐步探索 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 的整体设计非常的巧妙。

参考文档

相关推荐
luckywuxn9 分钟前
EurekaServer 工作原理
java·eureka
壹米饭12 分钟前
Java程序员学Python学习笔记一:学习python的动机与思考
java·后端·python
java金融14 分钟前
Java 锁升级机制详解
java
Young556617 分钟前
还不了解工作流吗(基础篇)?
java·workflow·工作流引擎
让我上个超影吧19 分钟前
黑马点评【缓存】
java·redis·缓存
ajassi200027 分钟前
开源 java android app 开发(十一)调试、发布
android·java·linux·开源
YuTaoShao41 分钟前
Java八股文——MySQL「存储引擎篇」
java·开发语言·mysql
crud1 小时前
Java 中的 synchronized 与 Lock:深度对比、使用场景及高级用法
java
王德博客1 小时前
【Java课堂笔记】Java 入门基础语法与面向对象三大特性详解
java·开发语言
seventeennnnn1 小时前
Java大厂面试真题:谢飞机的技术挑战
java·spring boot·面试·aigc·技术挑战·电商场景·内容社区