Java 动态代理

动态代理第一次遇到是研究Retrofit源码时,经过代码分析发现retrofit巧妙的运用了动态代理把请求接口类的方法的注解信息以及参数按照既定的规则组合成完整的请求。

动态代理的简单用例

一个简单的接口类

1
2
3
4
public interface Person {
void doThis(String thing);
void doThat(String thing);
}

可以看出该类只有一个简单的方法,

InvocationHandler 类的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PersonInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args){
if(method.getName().equals("doThis")){
System.out.println(args[0]);
return null;
}
if(method.getName().equals("doThat")){
System.out.println(args[0]);
return null;
}
return null;
}
public static void main(String[] args) {
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new PersonInvocationHandler());
person.doThis("I do this thing");
person.doThat("I do that thing");
}
}

输出结果为

1
2
I do this thing
I do that thing

通过输出接口分析,动态代理生成的对象调用方法时最终都会调用InvocationHandler的invoke方法,invoke(Object proxy, Method method, Object[] args) 可以看出 proxy是代理对象本身,method是调用的方法属性,而args数组就是方法参数。

动态代理的源码解析

我就从对象生成这句代码看起:

1
Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new PersonInvocationHandler());

从而找到实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler invocationHandler)
throws IllegalArgumentException {
if (invocationHandler == null) {
throw new NullPointerException("invocationHandler == null");
}
Exception cause;
try {
return getProxyClass(loader, interfaces)
.getConstructor(InvocationHandler.class)
.newInstance(invocationHandler);
} catch (NoSuchMethodException e) {
cause = e;
} catch (IllegalAccessException e) {
cause = e;
} catch (InstantiationException e) {
cause = e;
} catch (InvocationTargetException e) {
cause = e;
}
AssertionError error = new AssertionError();
error.initCause(cause);
throw error;
}

可以看出该方法主要是对异常情况进行处理,没有涉及到动态代码的原理。可以看出主要实现是getProxyClass方法,getProxyClass作用是获取代理类Class对象。getConstructor方法属于Class的方法,作用是获取构造函数,而newInstance的作用只是为了实例化一个代理类对象。以下查看getProxyClass方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//从interfaces可以看出,动态代理可以同时实现多个接口
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
throws IllegalArgumentException {
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
if (interfaces == null) {
throw new NullPointerException("interfaces == null");
}
final List<Class<?>> interfaceList = new ArrayList<Class<?>>(interfaces.length);
Collections.addAll(interfaceList, interfaces);
final Set<Class<?>> interfaceSet = new HashSet<Class<?>>(interfaceList);
if (interfaceSet.contains(null)) {
throw new NullPointerException("interface list contains null: " + interfaceList);
}
if (interfaceSet.size() != interfaces.length) {
throw new IllegalArgumentException("duplicate interface in list: " + interfaceList);
}
//查看缓存中是否已经存在代理类
synchronized (loader.proxyCache) {
Class<?> proxy = loader.proxyCache.get(interfaceList);
if (proxy != null) {
return proxy;
}
}
String commonPackageName = null;
for (Class<?> c : interfaces) {
//被代理类必须是接口类,这点感觉不好,如果任何类都可以代理那就很爽了
if (!c.isInterface()) {
throw new IllegalArgumentException(c + " is not an interface");
}
if (!isVisibleToClassLoader(loader, c)) {
throw new IllegalArgumentException(c + " is not visible from class loader");
},
if (!Modifier.isPublic(c.getModifiers())) {
String packageName = c.getPackageName$();
if (packageName == null) {
packageName = "";
}
if (commonPackageName != null && !commonPackageName.equals(packageName)) {
throw new IllegalArgumentException(
"non-public interfaces must be in the same package");
}
commonPackageName = packageName;
}
}
//可以看出改代码是获取所有接口的方法属性
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
//生成代理类的包名
String baseName = commonPackageName != null && !commonPackageName.isEmpty()
? commonPackageName + ".$Proxy"
: "$Proxy";
Class<?> result;
synchronized (loader.proxyCache) {
result = loader.proxyCache.get(interfaceList);
if (result == null) {
String name = baseName + nextClassNameIndex++;
//生成Class文件并生成代理类Class对象。
result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
loader.proxyCache.put(interfaceList, result);
}
}
return result;
}
1
2
3
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);

从以上代码可以看出动态代理最后的核心就是generateProxy,但它是本地方法,而且涉及动态产生Class文件,暂时不去涉猎,但基本已经搞清了动态代理的基本流程,如下;

1、调用 Proxy 的newProxyInstance 方法传入加载器ClassLoader对象,需要被代理的接口类的Class对象,以及InvocationHandler 的实例。
2、调用getProxyClass方法生成代理类Class文件,并加载生成Class对象,并返回。
3、然后通过Class对象进行实例化,产生代理类对象。
4、当调用代理类实例化对象之后,动态类对象就会回调InvocationHandler的invoke方法,并将方法属性、代理类对象已经参数传人,我们就可以在invoke方法中做统一处理。