博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Retrofit2 源码解析之动态代理
阅读量:6937 次
发布时间:2019-06-27

本文共 16744 字,大约阅读时间需要 55 分钟。

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

使用示例

public interface XinZhiWeatherApi {    @GET("{type}?key=xxxxxxxxxx&&language=zh-Hans&unit=c")    Flowable
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 create(final Class
service) { // 必须是接口类型且不能继承其它接口,否则抛出异常 Utils.validateServiceInterface(service); // validateEagerly 默认是 false if (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
serviceMethod = (ServiceMethod
) loadServiceMethod(method); OkHttpCall
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() {                @Override                public 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.$Proxy0super class of subject: Proxyinterface 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;        @Override    public 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; } @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) throws IllegalArgumentException { // invocationHandler 不能为null Objects.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
[], Class
> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); private static Class
getProxyClass0(ClassLoader loader, Class
... interfaces) { // 接口数量不能超过 65535,此处为 1 if (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
>> valuesMap = map.get(cacheKey); // step1. 初次调用,valuesMap 肯定为 null if (valuesMap == null) { // step2. 新建一个 Map 对象 ConcurrentMap
>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); // valuesMap 是刚 new 的,所以 supplier 肯定是 null Supplier
> supplier = valuesMap.get(subKey); // Factory 是 Supplier 的实现类,调用其 get() 时,内部会调用 ProxyClassFactory.apply() // 获得代理类的 Class 对象 Factory factory = null; while (true) { // step5. 第二次循环时,supplier 不为 null if (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 并赋给 supplier supplier = 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
// 为便于阅读,已将方法中所有泛型替换为具体类型        @Override        public synchronized Class
get() { Supplier
> supplier = valuesMap.get(subKey); // 此处为 == if (supplier != this) { return null; } Class
value = null; try { // 调用 valueFactory.apply(key, parameter) 生成并返回代理类的 Class 对象 // valueFactory 在 WeakCache 的构造器中传入,通过上面分析可知,其类型为 ProxyClassFactory value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { valuesMap.remove(subKey, this); } } assert value != null; CacheValue
> 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
@Override        public Class
apply(ClassLoader loader, Class
[] interfaces) { // interfaces 即为代理类要实现的接口,此处仅有一个接口需要实现 // IdentityHashMap 使用 == 比较 key,该 Map 很少使用 Map
, 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
methods = getMethods(interfaces); // 排序 Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); // 校验是否存在签名一致,仅返回值不同的方法,存在则抛出异常 validateReturnTypes(methods); // 读取抛出的异常列表 List
[]> 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高级特性与实践》第七章,里面会有详细介绍。

你可能感兴趣的文章
windows下 iis配置php5.6
查看>>
谷歌火狐浏览器保存网页为htm格式
查看>>
sqlserver表和库管理
查看>>
Ceph安装QEMU报错:User requested feature rados block device configure was not able to find it
查看>>
我的友情链接
查看>>
linux中 qt5 的环境搭建
查看>>
Qt 中调用cmd不显示窗口
查看>>
HBase原理和优化
查看>>
maven配置全局的jdk和配置局部的jdk
查看>>
Integer.valueOf(int i)源码
查看>>
CentOS 6.5 minimal 装配vmware-tools
查看>>
Linux中获取命令帮助
查看>>
从零构建PHP商用MVC框架(2.1 路由-带参数请求)
查看>>
CentOS用yum安装MariaDB
查看>>
ActiveMQ 全排序(Total Ordering)
查看>>
胖AP&瘦AP
查看>>
git 拉取问题
查看>>
清空weblogic日志文件nohup.out
查看>>
MyBatis注解select in参数
查看>>
禁止有道爬虫
查看>>