戏说代理模式和Java动态代理
代理
所谓代理,是为某一个实例的访问进行代理和控制。最好的例子是艺人和经纪人的关系,比如沟通档期是一个接口,那么艺人通常会通过经纪人去和第三方沟通档期相关的事情,这样的好处是,沟通档期的经纪人就会限制第三方对艺人的其它事情的打扰,只有沟通档期的请求才会被转发到艺人,其它事务都会通过 沟通档期接口进行限制。
对于演艺公司来说,静态代理就是在一开始为每一个艺人安排一个经纪人,这个经纪人专门负责一个艺人的相关事情。后来公司的管理层发现只有少数大明星的经纪人的事情比较复杂,但是对很多二线 三线的艺人来说,经纪人的事情可能基本上都差不多,但是公司却为每个人都指定了经纪人,导致成本较高(在编程里面表现为很多模板代码)。为了降低成本,公司改变了为每一个艺人都安排一个经纪人的方式,而是在有第三方来沟通艺人档期时,动态安排一个经纪人来为艺人服务,这就是动态代理。
静态代理比较简单,这里不展开描述,而JDK是从1.3版本开始支持动态代理,下面一起看看它的源码。如果你还不明白什么是代理 什么是代理类,建议先google一下。
Java动态代理源码分析
动态代理例子
首先我们还是通过一个简单的例子来说明动态代理是怎么一回事。
public class DynamicProxy {
public static void main(String[] args) {
Object proxyInstance = Proxy.newProxyInstance(MyInterface.class.getClassLoader(),
new Class[]{
MyInterface.class
},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("sum")) {
return (int)args[0]+(int)args[1];
}else if(method.getName().equals("returnMySelf")){
return proxy;
}
return null;
}
}
);
MyInterface myInterface = (MyInterface) proxyInstance;
System.out.println(myInterface.sum(1,2));
}
public static interface MyInterface {
int sum(int arg1,int arg2);
MyInterface returnMySelf();
}
}
可以看到,我们通过newProxyInstance
就产生了一个MyInterface
的实例,然后就可以通过myInterface.sum(1,2)
来调用sum()
方法,然后处理的结果就是我们在InvocationHandler
中处理的逻辑,是不是很神奇?下面我们一步步来分析源码是怎么做到的。
newProxyInstance方法
Proxy是实现动态代理的主要类,它提供来产生代理类和代理类实例的方法:newProxyInstance (ClassLoader loader, Class[] interfaces, InvocationHandler h)
,看看代码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//权限判断等,忽略
//生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
//调用构造器,生成实例
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
//各种异常处理
}
}
到getProxyClass0(loader, intfs)
中看看:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//一个代理类最多能够实现65535个接口
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 这里Proxy做了一次缓存,如果之前生成过这个Classloader和interfaces的代理类,那么这里直接返回
//否则新生成类的字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//将字节码加载到JVM
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
retrun proxyClass;
}
至此,我们生成了一个代理类,然后只需要将它实例化就可以了,可以看到代码中我们把参数InvocationHandler
传入了构造器中,这说明我们生成的代理类必然包含了一个接受InvocationHandler
的构造器,稍后我们会来分析这个生成的类。
newProxyInstance
这个方法实际上做了两件事:第一,创建了一个新的类【代理类】,这个类实现了Class[] interfaces
中的所有接口,并通过你指定的ClassLoader将生成的类的字节码加载到JVM中,创建Class对象;第二,以你传入的InvocationHandler
作为参数创建一个代理类的实例并返回。
代理类的字节码
上文已经说明了,代理类是通过ProxyGenerator.generateProxyClass(proxyName, interfaces);
生成的,这个方法会根据指定的包名和接口,生成一个代理类,我们来测试一下吧。
public class GenerateClass {
static interface Inter {
int sum(int arg1,int arg2);
}
public static void main(String[] args) throws IOException {
String path = File.separator+"Users"+File.separator+"pengliang"+File.separator+"InterImplProxy.class";
// String path = "c:"+File.separator+"ProxyImpl.class";
//很简单,生成一个实现了Inter接口的代理类的字节码
byte[] classCode = ProxyGenerator.generateProxyClass("com.chuyun.InterImplProxy.",new Class[] {Inter.class});
//字节码写入本地
FileOutputStream fileOutputStream = new FileOutputStream(path);
fileOutputStream.write(classCode);
fileOutputStream.close();
}
}
我们反编译一下这个InterImplProxy.class
文件(常见的反编译工具有:JD-GUI),我直接使用IDEA的插件来打开这个class文件,看到如下代码:
package com.chuyun.InterImplProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.GenerateClass.Inter;
//实现了Inter 并继承自Proxy ,Proxy有一个接受InvocationHandler的构造器
public final class InterImplProxy extends Proxy implements Inter {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
static {
try {
//反射获取这些函数描述
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.GenerateClass$Inter").getMethod("sum", new Class[]{Integer.TYPE, Integer.TYPE});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public InterImplProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
//将所有方法的调用转发给InvocationHandler的invoke方法
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} 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 int sum(int var1, int var2) throws {
try {
return ((Integer)super.h.invoke(this, m3, new Object[]{Integer.valueOf(var1), Integer.valueOf(var2)})).intValue();
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
ok,至此,是不是感觉动态代理也是一个很简单的东西?唯一的区别是我们静态代理中的代理类是由程序猿一开始定义的,而动态代理则是再运行时,通过InvocationHandler
来调用我们发送的调用。
总结
- 动态代理不支持代理抽象类 ,原因嘛,你看看上面生成的代码,代理类默认已经
extends Proxy
,Java
总不允许多重继承; - 动态代理生成的类如果实现的接口都是
public
,那么这个代理类就处于默认包中(最顶层包,没有包名);如果实现了包权限的接口,那么包名就是这个包权限所在的包名。 - 类名字格式为:
$ProxyN
其中N
为Proxy的计数器。之前我们说了,如果某一次动态代理的ClassLoader
和 接口个数和顺序完全一样,则Proxy
不会生成新的代理类。 -
InvocationHandler
的invoke
方法中的第一个参数就是这个代理类的实例,主要的用途呢就是为了解决有的接口返回的对象就是本身,见例子一中的MyInterface returnMySelf();
;但是这里有个需要注意的坑:
**
永远不要在InvocationHandler
的invoke
里面调用第一个参数proxy
的任何方法,因为这样会导致invoke
循环无数次调用,最终stackoverflow
。
**