反编译工具:http://www.javadecompilers.com/jad 使用方式请见Readme.txt,在windows下运行jad -sjava xxx.class就可以轻而易举的将class文件反编译成java文件,从而分析其具体的实现原理了。
android中很多地方都用的了动态代理,比如retrofit、插件化,那动态代理的原理是什么?为什么要使用动态代理?
我们依然从一个最简单的例子开始。
我们写一个接口:
public interface IUserService{
void login(String username, String password);
}
然后,利用动态代理去生成一个代理对象,去调用login方法:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class Test{
public static void main(String[] args){
IUserService userService = (IUserService) Proxy.newProxyInstance(IUserService.class.getClassLoader(),
new Class[]{IUserService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method = " + method.getName() +" , args = " + Arrays.toString(args));
return null;
}
});
System.out.println(userService.getClass());
userService.login("zhy","123");
}
}
好了,这应该是最简单的动态代理的例子了。
当我们去调研userService.login方法,你会发现InvocationHandler的invoke方法调用了,并且输出了相关信息。
怎么会这么神奇呢?
我们写了一个接口,就能产生一个该接口的对象,然后我们还能拦截它的方法。
继续看:
先javac Test.java,得到class文件。
然后调用:
java Test
输出
class com.sun.proxy.$Proxy0
method = login , args = [zhy, 123]
可以看到当我们调用login方法的时候,invoke中拦截到了我们的方法,参数等信息。
retrofit的原理其实就是这样,拦截到方法、参数,再根据我们在方法上的注解,去拼接为一个正常的Okhttp请求,然后执行。
想知道原理,根据我们枚举中的经验,肯定想看看这个
com.sun.proxy.$Proxy0 // userService对象输出的全路径
这个类的class文件如何获取呢?
很简单,你在main方法的第一行,添加:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
然后重新编译、执行,就会在当前目录看到了。
MacBook-Pro:tmp zhanghongyang01$ tree
.
├── IUserService.class
├── IUserService.java
├── Test$1.class
├── Test.class
├── Test.java
└── com
└── sun
└── proxy
└── $Proxy0.class
3 directories, 6 files
还是拿出我们刚才下载的jad吧。
执行:
jad -sjava com/sun/proxy/\$Proxy0.class
在jad的同目录,你就发现了Proxy0的java文件了:
package com.sun.proxy;
import IUserService;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements IUserService
{
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final void login(String s, String s1)
{
super.h.invoke(this, m3, new Object[] {
s, s1
});
}
private static Method m3;
static
{
m3 = Class.forName("IUserService").getMethod("login", new Class[] {
Class.forName("java.lang.String"), Class.forName("java.lang.String")
});
}
}
为了便于理解,删除了一些equals,hashCode等方法。
你可以看到,实际上为我们生成一个实现了IUserSevice的类,我们调用其login方法,实际上就是调用了:
super.h.invoke(this, m3, new Object[] {
s, s1
});
m3即为我们的login方法,静态块中初始化的。剩下是我们传入的参数。
那我们看super.h是什么:
package java.lang.reflect;
public class Proxy{
protected InvocationHandler h;
}
就是我们自己创建的InvocationHandler对象。