基于 Retrofit 2.3.0 & Android 8.1 分析 Java 动态代理在 Android 上的实现
未经允许不得转载

Retrofit 使用示例

public interface XinZhiWeatherApi {@GET("{type}?key=xxxxxxxxxx&&language=zh-Hans&unit=c")Flowable<WeatherBean> getNowWeather(@Path("type") String type, @Query("location") String location);
}public static XinZhiWeatherApi initWeatherApi() {HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).retryOnConnectionFailure(true).connectTimeout(10, TimeUnit.SECONDS).build();Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).client(client).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build();return retrofit.create(XinZhiWeatherApi.class);
}

如上所示,Retrofit 的使用非常简单。本文只关注 retrofit.create()

Retrofit.create

Retrofit.java

  public <T> T create(final Class<T> service) {// 必须是接口类型且不能继承其它接口,否则抛出异常Utils.validateServiceInterface(service);// validateEagerly 默认是 falseif (validateEagerly) {eagerlyValidateMethods(service);}// 返回动态生成的代理类return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() {private final Platform platform = Platform.get();@Override public 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);}if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);}});}

我们看到,Retrofit 的动态代理使用比较简单,不涉及接口的实现类,只有一个匿名的 InvocationHandler 类。下面写个示例代码(使用 IntelliJ IDEA 编写):

public interface ISubject {void sayHello();
}public static void main(String[] args) {ISubject subject = (ISubject) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{ISubject.class},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("InvocationHandler$invoke: call " + method.getName());return 0;}});System.out.println("class of subject: " + subject.getClass().getName());System.out.println("super class of subject: " + subject.getClass().getSuperclass().getSimpleName());System.out.println("interface implemented by subject: " + Arrays.toString(subject.getClass().getInterfaces()));System.out.println("fields of subject: " + Arrays.toString(subject.getClass().getDeclaredFields()));subject.sayHello();
}

一个非常简单的动态代理使用代码,只声明了一个接口,并没有实现这个接口,同 Retrofit 一样。日志输出如下:

class of subject: com.sun.proxy.$Proxy0
super class of subject: Proxy
interface implemented by subject: [interface com.yaren.proxy.ISubject]
fields of subject: [private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m1, private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m3, private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m2, private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m0]
InvocationHandler$invoke: call sayHello

从输出结果可以看出,动态代理对象 subject 的类名是 $Proxy1、父类是 Proxy、实现的接口是 ISubject,并且调用 subject 的 sayHello() 时,匿名内部类 InvocationHandler 的 invoke() 方法得到了调用。看到这里,已经能够猜出该代理类的大概实现了:

public class $Proxy1 extends Proxy implement ISubject {// sayHello() 对应的 Method 对象private static Method m1;@Overridepublic void sayHello() {try {// h 为 Proxy 中 InvocationHandler 类型的成员变量super.h.invoke(this, m1, (Object[]) null);} catch (Exception e) {e.printStackTrace();}}}

下面通过 ProxyGenerator.generateProxyClass() 生成动态代理类的字节码字节数组并存储为 .class 文件,最后反编译生成 .java 文件,验证一下上面的猜测:

    // subject 为上面生成的动态代理对象String proxyName = subject.getClass().getName() + ".class";byte[] clazz = ProxyGenerator.generateProxyClass(proxyName, new Class[]{ISubject.class});try {OutputStream out = new FileOutputStream(proxyName);InputStream is = new ByteArrayInputStream(clazz);byte[] buff = new byte[1024];int len;while ((len = is.read(buff)) != -1) {out.write(buff, 0, len);}is.close();out.close();} catch (IOException e) {e.printStackTrace();}

生成的文件为 com.sun.proxy.$Proxy0.class,反编译源码如下(为便于阅读,调整了变量和方法的顺序):

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package com.sun.proxy.$Proxy0;import com.yaren.proxy.ISubject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class class extends Proxy implements ISubject {private static Method m0;private static Method m1;private static Method m2;private static Method m3;static {try {m0 = Class.forName("java.lang.Object").getMethod("hashCode");m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.yaren.proxy.ISubject").getMethod("sayHello");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}public class(InvocationHandler var1) throws  {super(var1);}public final int hashCode() throws  {try {return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final boolean equals(Object var1) throws  {try {return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final String toString() throws  {try {return (String)super.h.invoke(this, m2, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final void sayHello() throws  {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}}

可以看到,除了 sayHello() 外,还重写了 equals()、hashCode()、toString()。以上是动态代理在 jdk 中的实现,因为 Android 中无法使用 sun.misc.ProxyGenerator 类,所以使用 java 环境来测试。接下来看动态代理在 Android 中的实现。

Proxy.newProxyInstance

Proxy.java

    // 动态代理类构造器的参数类型private static final Class<?>[] constructorParams = { InvocationHandler.class };// 在 newInstance() 中,会调用动态代理类的构造器传入 invocationHandler 对象protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{// invocationHandler 不能为nullObjects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();// 这句是关键:查找或生成代理类,cl 即为代理类的 Class 对象Class<?> cl = getProxyClass0(loader, intfs);try {// 调用动态代理类参数类型为 InvocationHandler 的构造器final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {cons.setAccessible(true);}// 返回动态代理对象return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}

Proxy.getProxyClass0

Proxy.java

    // 缓存动态代理类 Class 对象的查找工厂类(即 WeakCache$Factory)// WeakCache 有三个泛型,第一个为 Key,第二个为 SubKey, 第三个为 Value// 此处 Key 为 classloader,SubKey 为 KeyFactory( KeyFactory 用来根据被代理的接口生成 subKey),// Value 为 动态代理类的类类型// ProxyClassFactory 用于生成动态代理类的 Class 对象 private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {// 接口数量不能超过 65535,此处为 1if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// 如果由该 classloader 加载的实现给定 interfaces 的代理类的 Class 对象已存在,直接返回缓存的 Class 对象,// 否则调用 ProxyClassFactory 生成新的 Class 对象return proxyClassCache.get(loader, interfaces);}

WeakCache.get

WeakCache.java

    // 为便于阅读,已将方法中所有泛型替换为具体类型public Class<?> get(ClassLoader key, Class<?>[] parameter) {Objects.requireNonNull(parameter);expungeStaleEntries();Object cacheKey = CacheKey.valueOf(key, refQueue);ConcurrentMap<Object, Supplier<Class<?>>> valuesMap = map.get(cacheKey);// step1. 初次调用,valuesMap 肯定为 nullif (valuesMap == null) {// step2. 新建一个 Map 对象ConcurrentMap<Object, Supplier<Class<?>>> oldValuesMap= map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());if (oldValuesMap != null) {valuesMap = oldValuesMap;}}Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));// valuesMap 是刚 new 的,所以 supplier 肯定是 nullSupplier<Class<?>> supplier = valuesMap.get(subKey);// Factory 是 Supplier 的实现类,调用其 get() 时,内部会调用 ProxyClassFactory.apply()// 获得代理类的 Class 对象Factory factory = null;while (true) {// step5. 第二次循环时,supplier 不为 nullif (supplier != null) {// step6. 调用实现类 factory 的 get(), 返回代理类的 Class 对象Class<?> value = supplier.get();if (value != null) {return value;}}if (factory == null) {// step3. 新建 Factory 对象 factory = new Factory(key, parameter, subKey, valuesMap);}if (supplier == null) {// step4. 将 factory 关联至 subkey 并赋给 suppliersupplier = valuesMap.putIfAbsent(subKey, factory);if (supplier == null) {supplier = factory;}} else {if (valuesMap.replace(subKey, supplier, factory)) {supplier = factory;} else {supplier = valuesMap.get(subKey);}}}}

Factory.get

WeakCache$Factory.java

        // 为便于阅读,已将方法中所有泛型替换为具体类型@Overridepublic synchronized Class<?> get() {Supplier<Class<?>> supplier = valuesMap.get(subKey);// 此处为 ==if (supplier != this) {return null;}Class<?> value = null;try {// 调用 valueFactory.apply(key, parameter) 生成并返回代理类的 Class 对象// valueFactory 在 WeakCache 的构造器中传入,通过上面分析可知,其类型为 ProxyClassFactoryvalue = Objects.requireNonNull(valueFactory.apply(key, parameter));} finally {if (value == null) {valuesMap.remove(subKey, this);}}assert value != null;CacheValue<Class<?>> cacheValue = new CacheValue<>(value);if (valuesMap.replace(subKey, this, cacheValue)) {reverseMap.put(cacheValue, Boolean.TRUE);} else {throw new AssertionError("Should not reach here");}return value;}

ProxyClassFactory.apply

Proxy$ProxyClassFactory.java

        @Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {// interfaces 即为代理类要实现的接口,此处仅有一个接口需要实现// IdentityHashMap 使用 == 比较 key,该 Map 很少使用Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) {Class<?> interfaceClass = null;try {interfaceClass = Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}// 类类型不等,抛出异常if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}//  不是接口类型,抛出异常if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}// 数组中存在重复接口,抛出异常if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}// 代理类包名String proxyPkg = null;int accessFlags = Modifier.PUBLIC | Modifier.FINAL;// 记录所有非 public 接口的包名,以便代理类可以定义在相同目录下。// 校验所有非 public 接口是否在同一个目录下,不是则抛出异常。for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags = Modifier.FINAL;String name = intf.getName();int n = name.lastIndexOf('.');String pkg = ((n == -1) ? "" : name.substring(0, n + 1));if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}// 我们接口是 public 的,所以包名为 ""if (proxyPkg == null) {proxyPkg = "";}{// Android 平台直接调用 native 代码生成代理类 Class 对象,而不是像 jdk 一样,通过 ProxyGenerator 类。List<Method> methods = getMethods(interfaces);// 排序Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);// 校验是否存在签名一致,仅返回值不同的方法,存在则抛出异常validateReturnTypes(methods);// 读取抛出的异常列表List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);Method[] methodsArray = methods.toArray(new Method[methods.size()]);Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);// 生成代理类的名称,此处为 "$Proxy1"long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;// 调用 native 代码生成代理类 Class 对象return generateProxy(proxyName, interfaces, loader, methodsArray,exceptionsArray);}}

结语

看完本文,应该可以对 Retrofit 所使用的动态代理技术有了更深入的理解。至于 jvm 如何加载、链接、初始化代理类,则不在本文讨论范围。感兴趣的可以阅读《深入Java虚拟机_JVM高级特性与实践》第七章,里面会有详细介绍。

Retrofit2 源码解析之动态代理相关推荐

  1. Spring AOP源码解析——AOP动态代理原理和实现方式

    2019独角兽企业重金招聘Python工程师标准>>> Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和 ...

  2. Retrofit2源码解析——网络调用流程(下)

    Retrofit2源码解析系列 Retrofit2源码解析(一) Retrofit2源码解析--网络调用流程(上) 本文基于Retrofit2的2.4.0版本 implementation 'com. ...

  3. Retrofit2源码解析

    一.前言: Retrofit: 一个 Restful 设计风格的 HTTP 网络请求框架的封装.基于 OkHttp 严格地说,Retrofit2并不是一个网络请求交易框架,它只是对网络请求框架的封装. ...

  4. 设计模式 结构型模式 -- 装饰者模式(概述 快餐店案例 模式优点 使用场景 源码解析 BufferedWriter 和代理模式的区别)

    1. 装饰者模式 1.1 概述 我们先来看一个快餐店的例子: 快餐店有炒面.炒饭这些快餐,可以额外附加鸡蛋.火腿.培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得 ...

  5. Retrofit2 源码解析

    Retrofit是什么 简单的说它是一个基于OkHttp的RESTFUL Api请求工具,从功能上来说和Google的Volley功能上很相似,但是使用上很不相似. Retrofit怎么用 比如你要请 ...

  6. Retrofit2源码解析(一)

    本文基于Retrofit2的2.4.0版本 implementation 'com.squareup.retrofit2:retrofit:2.4.0' Retrofit2底层基于OkHttp3,是对 ...

  7. 美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇

    大家好,这篇文章我们来聊下动态线程池开源项目(DynamicTp)的通知告警模块.目前项目提供以下通知告警功能,每一个通知项都可以独立配置是否开启.告警阈值.告警间隔时间.平台等,具体代码请看core ...

  8. Spring Cloud Alibaba源码 - 22 Feign 源码解析

    文章目录 Feign 源码 核心思想 --- 动态代理 Feign 源码 核心思想 - 动态代理 呶 ,图又又又给你画好了, https://www.processon.com/view/link/6 ...

  9. Redis源码解析(15) 哨兵机制[2] 信息同步与TILT模式

    Redis源码解析(1) 动态字符串与链表 Redis源码解析(2) 字典与迭代器 Redis源码解析(3) 跳跃表 Redis源码解析(4) 整数集合 Redis源码解析(5) 压缩列表 Redis ...

最新文章

  1. sqoop架构_SQOOP架构的深入介绍
  2. C# 模拟 Post
  3. 使用命令行对Android应用签名
  4. egret:什么是脏矩形
  5. unity让对象作为参数_unity-container – 一个unity容器可以将自身的引用作为构造函数参数传递吗?...
  6. 计算机考试前的心情作文,考试时的心情作文(七篇)
  7. 在Spring-boot中,为@Value注解添加从数据库读取properties支持
  8. python websocket爬虫_Python如何爬取实时变化的WebSocket数据
  9. java.util.regex.PatternSyntaxException: Dangling meta character '?' near index 0特殊字符表达
  10. commons-pool2-2.3 jar包_Maven项目中引入net.sf.json.JSONObject依赖jar包
  11. LNMP详解(十四)——Nginx日志详解
  12. matlab 绘图 模板,【科研绘图】MATLAB可视化代码模板
  13. 简单使用pdf插件pdf.js
  14. Day06(上)C++继承和派生
  15. nodeJS实现简单网页爬虫功能
  16. html表格边框默认值,table表格边框的设置
  17. Objective-C面向对象
  18. Linux (redhat)封装虚拟机镜像
  19. python排序输出人名_005_015 Python 人名按字母排序,首字母分组 | 学步园
  20. mysql权限模型子查询 casewhen子查询

热门文章

  1. python 生产消费者_python之生产者消费者模型实现详解
  2. aac蓝牙编解码协议_蓝牙音频编码哪个音质好?今天我们来逐一解读
  3. android如何暂停倒计时,在Android中暂停和恢复倒计时器和进度条?
  4. html5新特性 移除哪些,html5\CSS3有哪些新特性、移除了哪些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分HTML和HTML5?...
  5. php传值到模板,laravel 实现向公共模板中传值 (view composer)
  6. 本弗莱数据可视化的生产流程图_力控锂离子电池车间数据采集系统
  7. 香河php程序员_失控的香河最流行的四大职位
  8. 慧鱼机器人编程语言的特点_慧鱼机器人课程设计报告.doc
  9. php字符串中有特殊符号怎么处理,PHP字符串中特殊符号的过滤方法介绍
  10. java爬虫面试题_使用Java实现网络爬虫