动态代理
1.静态代理
先看下代码:
interface IHttp {
void get();
}
class AHttp implements IHttp {
@Override
public void get() {
}
}
class HttpProxy implements IHttp {
AHttp aHttp = new AHttp();
@Override
public void get() {
Log.d(...)
aHttp.get();
}
}
private void test() {
IHttp iHttp = new HttpProxy();
iHttp.get();
}
静态代理的好处是:
在不改动原”实现方法”(aHttp.get())的情况下,直接插入”额外”的处理(Log.d(…))。
静态代理存在缺点:
假设存在IHttp2,IHttp3..等多个类似的接口,都需要在方法体里面打印Log日志(共同的需求),那么必须分别实现相应的HttpProxy代理类,不够灵活。
2.动态代理 动态代理可以弥补这个缺陷,代码如下
private void test() {
// IHttp iHttp = new HttpProxy();
// iHttp.get();
AHttp aHttp = new AHttp();
//newProxyInstance可以创建一个实现了IHttp接口的对象
IHttp iHttp2 = (IHttp) Proxy.newProxyInstance(IHttp.class.getClassLoader(), new Class[]{IHttp.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("IHttp", "执行HttpProxy..");
aHttp.get();
return proxy;
}
});
iHttp2.get();
}
当 iHttp2.get()的时候,实际执行了InvocationHandler的invoke函数。使用动态代理,完全看不到代理类的生成过程,但确实在invoke()中完成了对get()函数的代理,这是怎么做到的呢,代理类又是怎么生成的呢?
我想只能查看Proxy.newProxyInstance一探究竟。
简化一下源码:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {
final Class<?>[] intfs = interfaces.clone();
//获取代理类的Class类型
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
cons.setAccessible(true);
//利用反射,创建代理类对象
return cons.newInstance(new Object[]{h});
}
探究一下getProxyClass0()具体是怎么做的,这里用到了一个WeakCache对象,它是所有代理类的缓存,如果已经有了该代理类就直接使用,没有才会进行创建,然后缓存起来。
那么无缓存的时候具体怎么创建代理类呢,关键代码:
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
subKeyFactory实际是Proxy的内部类ProxyClassFactory,重写了apply方法:
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 1.对classLoader和interface做校验
// 2.获取目标接口(被代理方)的method相关属性,主要是返回值和Exception
// 3.对准备创建的代理类命名,$Proxy+num(已经创建的代理类数量)
// 4.最后通过generateProxy()函数创建字节码文件并创建出Class对象。generateProxy是一个native函数
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
3.Android中常用的库Retrofit2就使用了动态代理
通常情况下,网络接口的请求方法都写在一个单独的接口中,比如(以下retrofit:2.8.1):
public interface IApiService {
@GET("art/artlist")
Flowable<BaseResponse<List<CodeArticleBean>>> getCodeDatas(@Query("page") int page,
@Query("row") int row,
@Query("type") int type);
}
// getCodeDatas中包含了Http请求的各种信息,GET请求,接口Path,请求参数等,调用getCodeDatas的过程中,使用了动态代理:
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
//通过反射获取method的注解,参数等。
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
在invoke()函数中统一处理Http的请求信息,由于只需要目标类IApiService中的方法注解和参数信息,所以这里不需要实例化IApiService对象(对比2中的AHttp实例)。顺带提一下,由于代理类是运行时系统生成的,所以Retrofit2中的接口参数的注解是RUNTIME运行时注解。
4.Debug查看代理类
回归上面2中的的例子,简单的修改一下
private void test() {
//代理1
IHttp iHttp1 = (IHttp) Proxy.newProxyInstance(IHttp.class.getClassLoader(), new Class[]{IHttp.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("IHttp", "执行了iHttp1");
return proxy;
}
});
//代理2
IHttp2 iHttp2 = (IHttp2) Proxy.newProxyInstance(IHttp2.class.getClassLoader(), new Class[]{IHttp2.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("IHttp", "执行了iHttp2");
return proxy;
}
});
iHttp1.get();
iHttp2.post();
}
运行后:
可见生成的代理类分别为$Proxy0.class和$Proxy1.class,且位于项目的包名根目录下。