前言
如果我们是做SDK开发,最常用的架构就是MVC。在MVC中,我们经常会提供给外部C,用于让外部调用我们SDK暴露的功能。如果我们的V,要添加一个按钮,或者V的视图滑动事件,要提供给C去暴露出去,V和C基本要同时写2个方法,比如addButton(Button btn)。这个时候我们发现问题了,每次我们要去提供V、M的功能暴露出去,就要同时去C里面实现一个方法。
那有没有一个方法能够让V、M修改时,C里面有动态的添加添加一个方法。
有。那就是动态代理。
本篇文章会先讲解一个动态代理的例子,然后再从源码剖析动态代理的实现。
正片
我们举个例子,比如我们的老板(boss)想要去买个东西,他可以让他员工(staff)去帮他买东西,那么这里员工(staff)就是老板(boss)的代理。
step1 实现老板类
那么我们先实现一个老板类Boss,实现一个buy买东西的方法。
public class Boss implements IBossImpl {
@Override
public String buy(Object things) {
return "老板买"+things;
}
}
然后我们再实现一个员工类(StaticProxyStaff),实现老板(boss)的方法接口(IBossImpl),可以看到员工买东西的时候,实际是调用的boss买东西,那么员工就代理了boss的购买方法。
public class StaticProxyStaff implements IBossImpl {
Boss boss;
public StaticProxyStaff(Boss boss) {
this.boss = boss;
}
@Override
public String buy(Object things) {
return boss.buy(things);
}
}
如果我们老板现在想要发送邮件呢?那么代理类还要实现邮件方法
那么我们如果用动态代理怎么操作呢?
step2 proxy.newProxyInstace()
我们可以看到先通过Proxy.newProxyInstance(classloader, class[], invocationHandler)生成一个接口类对象,然后调用员工(staff)购买和发送email的方法
IBossImpl staff = (IBossImpl) Proxy.newProxyInstance(DProxyTest.class.getClassLoader(),
new Class[]{IBossImpl.class}, new Staff(new Boss(), "小秘"));
staff.buy("奶茶");
staff.email("[email protected]");
step3 InvocationHandler
我们实现一个Staff类继承InvocationHandler,其中最重要的方法就是invoke(proxy, method, args)方法,这里我们直接调用boss对象的方法。
public class Staff implements InvocationHandler {
private String name;
private Boss boss;
public Staff(Boss boss, String name) {
this.boss = boss;
this.name = name;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(boss, args);
System.out.println(name+"替"+object);
return object;
}
}
结果
源码解读:
等不及的朋友可以翻到最下面看总结。
上面我们能够看到最重要的方法其实是Proxy.newProxyInstance(classloader, class[], invocationHandler),然后生成一个接口对象,至于这个对象是什么,其实我们并不知道。现在我们去看一下这个对象是什么?
我们可以看到这个对象是com.sun.proxy.$Proxy0,可以看到这个对象是可以强转为IBossImpl接口,所以可以说明这个com.sun.proxy.$Proxy0对象是继承IBossImpl接口的。
那么这个对象又是怎么生成的呢?我们看下proxy.newProxyInstace()方法。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
//查找并生成对应的class
Class<?> cl = getProxyClass0(loader, intfs);
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
//如果构造方法不是public 修改权限
cons.setAccessible(true);
}
//构造函数,传入InvocationHandler构造对象
return cons.newInstance(new Object[]{h});
}
...
}
我们可以看到最重要的是getProxyClass0方法,可以看到getProxyClass0生成了一个类,然后这个类的构造函数需要传入一个invocationHandler对象。那么我们能猜到它的内部肯定是操作invocationHandler这个接口。
那么我们先来看下这个代理类的类信息,可以看到成员变量里面有5个Method类型成员变量,然后是集成Proxy类和IBossImpl接口的,他的构造函数里面有一个invocationHandler,其中有5个方法,equals、toString、hashcode、email(IBossImpl里面的方法)、buy(IBossImpl里面的方法)。看吧,果然构造函数需要传入一个invocationHandler,然后我们再来看下这个类是怎么生成的?
我们来看下getProxyClass0内部干了什么?调用的是proxyClassCache.get(classloader, interfaces)方法。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//如果代理类在缓存里面存在直接返回,不然从ProxyClassFactory创建
return proxyClassCache.get(loader, interfaces);
}
step4 proxyClassCache.get(classloader, interfaces)
我们可以看到返回的值是supplier.get()方法返回的,那么supplier又是什么呢?可以看到其实是新建的factory。我们看下factory的get方法。
public V get(K key, P parameter) {
...
while (true) {
if (supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// 成功安装factory
supplier = factory;
}
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
supplier = valuesMap.get(subKey);
}
}
}
}
step5 Factory.get()
可以看到valueFactory.apply()方法,我们看下这个方法
V get(){
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
...
return value;
}
step6 ProxyClassFactory.apply(ClassLoader loader, Class<?>[] interfaces)
可以看到类的名字是com.sun.proxy.&Proxy数字,类是由Proxy.defineClass0(var1, var23, var22, 0, var22.length)生成的,其中的var22是由ProxyGenerator.generateProxyClass(var23, var2, var17)生成的byte[]。defineClass是一个native方法,我们来看一下ProxyGenerator.generateProxyClass()方法。
public Class<?> apply(ClassLoader var1, Class<?>[] var2){
IdentityHashMap var3 = new IdentityHashMap(var2.length);
Class[] var4 = var2;
int var5 = var2.length;
//检查类是否已经被classloader加载
for(int var6 = 0; var6 < var5; ++var6) {
Class var7 = var4[var6];
Class var8 = null;
try {
var8 = Class.forName(var7.getName(), false, var1);
} catch (ClassNotFoundException var15) {
;
}}
}
if(var16 == null) {
var16 = "com.sun.proxy.";
}
long var19 = nextUniqueNumber.getAndIncrement();
String var23 = var16 + "$Proxy" + var19;
byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);
try {
return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
} catch (ClassFormatError var14) {
throw new IllegalArgumentException(var14.toString());
}
}
step7 ProxyGenerator.generateProxyClass(var23, var2, var17)
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
return var4;
}
step8 ProxyGenerator.generateClassFile()
private byte[] generateClassFile() {
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if(this.methods.size() > '\uffff') {
throw new IllegalArgumentException("method limit exceeded");
} else if(this.fields.size() > '\uffff') {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
总结
1.动态代理使用时,需要传入一个接口A和InvocationHandler,生成一个继承Proxy、实现接口A的类的对象。
2.生成一个byte[],其中继承接口A并且实现方法,然后通过native方法Proxy.defineClass0()生成class。
3.每个方法内部调用invocationHandler的invoke(Object proxy, Method method, Object[] args)方法,其中proxy是生成的代理本身,method是接口的method,args被代理对象方法中传入的参数。
参考:
JDK动态代理[3]----WeakCache缓存的实现机制
JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析
完整Android学习路径 请戳我的Android学习之旅(持续更新中...)
从源码角度分析Activity的生命周期怎么触发的(onCreate onStart onResume onPause onStop onDestroy)(附测试代码)
基于AIDL的 Activity、Service跨进程观察者模式实现与源码解读
走进源码,Android面试最常见Handler、Looper、Message问题总结与解答