上一篇既然是静态代理,那么必然会有一个概念是动态代理,静态代理也是为了给这一篇动态代理做的铺垫;常年混迹Android开发群的我,总会遇到有朋友提问面试被问到Retrofit请求框架里面怎么实现解耦的,在我正准备回答“不就是创建一个Retrofit对象,然后定义一个接口,在请求方法上写上对应的注解就搞定了”的时候,大佬回答一个“动态代理”瞬间把我弄懵了,???啥玩意,动态代理是啥,不知道有多少人跟我一样,从0开始写了好几年Android代码,连动态代理是啥都不知道...
动态代理
虽然是动态,但是最终还是一个代理,所以最基本的元素跟上一篇静态代理是一致的,需要一个定义行为的接口,一个代理类和一个被代理类,使用过Retrofit的朋友都知道,基本的使用方法里面,我们需要定义一个对于网络请求方法的接口,并使用注解(注解后面找时间写)将这个请求所需要的参数,地址,返回类型进行定义
上层接口
/**
* 代理模式中的行为规范接口
*/
public interface IHttpApis {
@GET("api://test")
Object test(@Query("param") String param);
}
被代理对象
Retrofit框架本质上是对OkHttp框架的进一步封装,跟网络请求相关的(寻址,建立连接,发送报文等...)都是由OkHttp去进行的,Retrofit我的理解是对我们在使用OkHttp是需要自己去实例化Call对象这部分分发逻辑进行了一个封装,通过注解和动态代理,更加的简洁方便,所以被代理对象,是其内部的OkHttp部分
//Retrofit通过Builder建造者模式进行初始化,在使用的时候传入了OkHttpClient对象
/**
* The HTTP client used for requests.
* <p>
* This is a convenience method for calling {@link #callFactory}.
*/
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
//使用Retrofit时构建Retrofit对象
Retrofit.Builder()
.baseUrl("")
//这是传入的被代理对象
.client(new OkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
代理对象
按照静态代理的逻辑,我们还需要一个代理对象,用于在model层进行方法调用,那么我们看看动态代理是怎么玩的
//实例化代理对象
IHttpApis apis = (IHttpApis) Proxy.newProxyInstance(IHttpApis.class.getClassLoader(),
new Class<?>[]{IHttpApis.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
//使用代理对象
apis.test("param");
???这是咋操作的,我第一次看到动态代理整个人都懵了,还可以跳过new关键字实例化对象
按照多年的Android开发经验,实例化对象都是通过new关键字进行的,动态代理的关键点就在于不需要手动写代理类的.java文件,这部分需要插播一点jvm的类加载机制
通过手写.java类的方式
- java类通过javac程序编译成.class字节码对象
- .class对象通过类加载器加载进入jvm的方法区
- 从方法区中拿到对象的指向,在堆中实例化对象
没去学习动态代理之前,一直使用这样的写代码方式,从jvm看,javac程序将.java文件编译成.class文件,然后通过类加载器加载到内存中,然后在程序运行的时候,通过new关键字就可以在方法区中找到对应的类结构,就可以用来实例化对应的代理类对象,但是这这需要有一个我们写的.java文件去进行编译,而动态代理跳过了这一步,在不需要我们手动写.java代理类文件的情况下,也可以让我们获得一个实例化的代理类对象,并可以通过这个对象去执行相应的方法,那看看对应的源码,看看是怎么绕过前面的编译过程,在运行时实例化对象的,具体的使用就是通过Proxy.newProxyInstance()方法开始的
/**源码比较长,省略掉不必要的代码逻辑*/
// ->$Proxy.newProxyInstance()
//步骤1
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
//...省略掉不必要的代码合注释
//1.获得class对象,cl对象就是实际的代理对象 proxyPkg$Proxy0
final Class<?>[] intfs = interfaces.clone();
//步骤2
Class<?> cl = getProxyClass0(loader, intfs);
//获取对应class对象的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
//构造函数传参
final InvocationHandler ih = h;
//根据构造函数,反射获取对象,将传入的InvocationHandler参数传如构造函数里面了
return cons.newInstance(new Object[]{h});
}
/****************跟进分割*******************/
//->$Proxy. getProxyClass0
//class对象通过proxyClassCache中get()获取,这里cache对象是对该接口代理做了一个缓存
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//步骤3
return proxyClassCache.get(loader, interfaces);
}
/****************跟进分割*******************/
//proxyClassCache.get是一个WeakCache的实例
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
//WeakCache的构造方法,和get()方法
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
public V get(K key, P parameter) {
//...
//从缓存中查找
Object cacheKey = CacheKey.valueOf(key, refQueue);
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//缓存中没有的时候,通过subKeyFactory去获取缓存的key, subKeyFactory是构造方法传入的
//通过其获取一个key,这个key使用的是传入接口class对象的hashCode
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
//map中第一次没缓存,走后门的循环中初始化supplier,就是一个
if (supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
//第一次循环factory为null,需要初始化一个Factory,在下面赋值给supplier
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
//下次循环直接使用
supplier = factory;
}
} else {
...
}
}
}
/****************跟进分割*******************/
//->$WeakCache->$Factory.get()
private final class Factory implements Supplier<V> {
@Override
public synchronized V get() {
//通过valueFactory.apply()去初始化对象,就是走ProxyClassFactory().apply()方法
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
return value;
}
}
/****************跟进分割*******************/
//->$Proxy->$ProxyClassFactory.apply()
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 代理类实例化对象命名拼接规则
private static final String proxyClassNamePrefix = "$Proxy";
//用于递增的序号
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
...
//如果不是接口,抛出异常,代理模式的三要素之一的接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
}
...
//省掉对接口class的解析
//代理类名字拼接规则 类名proxyPkg + $Proxy + 自增序号0开始
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//最后传入了需要实例化的代理类类名,需要被代理的接口名,还有类加载器等参数
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
//Android实例化类的方法是调用了native方法
@FastNative
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
追到最后,Android端动态代理最后执行了一个native方法,具体的把class对象字节码的生成和加载到内存的操作放在了native函数中,根据jvm的规范,就可以使用类加载器进行反射获取构造函数,然后就可以就可以实例化代理对象了
native方法暂时就就不跟了,动态生成class字节码可以在Java工程中的动态代理源码看一下,在生成了代理对象的命名后,执行了下面的逻辑
//生成代理类名称
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//ProxyGenerator.generateProxyClass生成代理类的class字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//将class加载到jvm内存中
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
}
原来可以通过ProxyGenerator对象可以在运行时生成一个class字节码文件,然后动态的加载进入内存,通过代码查看一下生成的class对象
byte[] bytes = ProxyGenerator.generateProxyClass("ProxyImpl", new Class[]{IHttpApis.class});
File file = new File(".../ProxyImpl.class");
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
//对应的接口
public interface IHttpApis {
public void get(String param);
}
//下面就是生成的ProxyImpl.class文件
public final class ProxyImpl extends Proxy implements IHttpApis {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public ProxyImpl(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} 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 get(String var1) throws {
try {
//实际执行方法调用的位置
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
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.maniu.proxy.IHttpApis").getMethod("get", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到,生成的代理类,的构造函数会有一个InvocationHandler参数传入,然后会将实现的对应接口内声明的方法,定义为Method成员对象,其中也会含有其父类的方法,然后通过方法名和参数类型进行匹配,最终在调用该方法的时候,实际上是调用的h(InvocationHandler)的invoke方法,并将参数传递出去
以上就是动态代理模式下,通过工具类在代码运行时动态的生成class文件并加载到jvm中的流程,然后调用方法时,最终会执行InvocationHandler的invoke方法,并将参数传递出去
Retrofit中的动态代理
对动态代理有了了解,那么回看Retrofit中怎么使用的动态代理
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("")
.client(new OkHttpClient())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
//本文开始的时候Retrofit的使用方法,会调用对用的create()方法,传入定义的接口
IHttpApis apis = retrofit.create(IHttpApis.class);
apis.test("params");
Retrofit中create()方法的
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
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, 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);
}
});
}
就是使用了动态代理的调用方式,返回了一个动态生成的代理对象$Proxy0,然后传入了InvocationHandler实例,那么在方法调用的时候,最终就会回调执行invoke()方法,然后在里面进行注解的解析,地址的拼接和OkHttp的调用(这部分后面找机会再分析)
小结
动态代理也是代理模式,只是由我们在编码期需要去写的代理类.java文件在运行时直接生成了class字节码文件,并加载到内存中,然后利用反射的原理获取构造方法对其进行实例化,返回实例好的代理对象,然后通过这个代理对象进行方法的调用,最终执行到传入的InvocationHandler的invoke方法中
通过代理模式,我们可以哎代理方法invoke()执行前后插入逻辑,多方法进行增强,例如在Retrofit中,就可以将生成OkHttp请求需要的Call对象和分发部分进行抽离,在使用中只需要声明一个接口方法,再利用注解传参,将更多的细节进行解耦,使用更加方便
虽然在项目中我还没有实际使用到(毕竟是个菜狗),但是整个动态代理的流程还是梳理清楚了,通过这样的梳理,以后在群里有人再问Retrofit的思想的时候,就可以装逼了,欢迎路过的大佬进行指点,可以给我分享一些常见的使用场景,而不是用于面试答疑