动态代理是对代理模式的一种实现,JDK支持动态代理。静态代理是由编写人员自己编写编译,动态代理的代理类则是由代码帮助生成。本文将探索如何使用动态代理以及其原理,做到知其然知其所以然。
Java动态代理相关的类和接口:
java.lang.reflect中的InterfaceInvocationHandler,
其中有且仅有有 Objectinvoke(Object proxy,Method method,Object[] args) throws Throwable这一个方法
Proxy类
其中提供了四个静态方法
getInvocationHandler(Objectproxy) 获取代理对象的handler
getProxyClass(ClassLoaderloader,Class<?>… interfaces) 获取代理类,需要传入加载代理类的类加载器和需要代理类实现的一组接口
isProxyClass(Class<?>cl) 检查某个类是否是代理类
newProxyInstance(ClassLoaderloader,Class<?>[] interfaces,InvocationHandler h) 被委托类(真实类)的类加载器,需要代理类实现的一组接口,关联的handler,这个方法是对其他方法的封装,返回一个代理类的实例。
每个代理类实例都有一个实现了invocationhandler的对象与之关联,代理实例会调用关联的Handler的invoke方法,最大问题出现---代理类在哪里?
在代码中,使用JDK提供的动态代理还是相当的简便,代码如下:
公共接口:
public interface ShouldDoSomeThing {
/**
* 实现此接口的类,需要实现这个方法
*/
public void doSomeThing();
}
Handler类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class HandlerClass implements InvocationHandler{
private Object o;
public HandlerClass(Object o){
this.o = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("do before!");
method.invoke(o, args);
System.out.println("do after!");
return null;
}
}
需要特别提醒:这个handler类在上面已经有叙述,其并不是代理类。
然后是客户端类:
public class Main {
public static void main(String[] args) {
RealClass rc = new RealClass();
HandlerClass pc = new HandlerClass(rc);
//这里必须转型到某个接口
ShouldDoSomeThing ProxyTest = (ShouldDoSomeThing) Proxy.newProxyInstance(rc.getClass().getClassLoader(), rc.getClass().getInterfaces(), pc);
ProxyTest.doSomeThing();
}
}
输出:
do before!
do some thing!
do after!
从这里可以看出,代理类实例可以安全的转型到被委托类实现了接口的类型。那代理类到底在哪里?输出为什么会是这样?
首先根据客户端的使用流程说下整个过程:
被委托类对象被创建,这个类是实际操作的执行者。然后Handler被创建,并且传入了被委托类对象,接下来,proxy对象被创建。
调用doSomeThing方法,此时,代理类对象所实现的相应的接口方法被调用,在代理类方法的内部,调用了与之相关联的handler的invoke方法,而invoke方法中调用了被委托类的相应接口的方法,所以实际的操作被执行,相应在invoke方法中附加的相关操作也被执行。
上面的描述,可以使用一个UML图来表示:
图中展示了整个动态代理的过程,可以看出,代理类是由Proxy来创建,而且客户端是和代理类交流,可见,JDK的动态代理是严格遵守代理模式。其中的$ProxyN虽然是个类,但是其在外存上并不会存在.class文件,其在运行过程中动态生成,存在于内存中的某个角落。但是要理解RealClass是如何和Handler关联,以及代理类是如何调用invoke方法,还必须深入到源代码。
首先看看创建Proxy对象的源代码
/**
*
* @param loader 类加载器
* @param interfaces 需要代理类所实现的一组接口
* @param h 与代理类关联的handler(需要执行invoke方法的handler对象)
* @return 代理类对象
* @throws IllegalArgumentException
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
// 安全检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
}
/*
* 查看是否已经存在代理类,如果存在,就返回它,如果不存在,就创建
*/
Class<?> cl = getProxyClass0(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//这是关键代码,首先获得了其构造器,带有h参数的那个构造器
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) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
接下来去看看如何创建的代理类$ProxyN:
首先是getProxyClass0
/*
*
* @param loader 类加载器
* @param interfaces 需要实现的一组接口
* @return 代理类的Class对象
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//如果在map中存在,就返回,不存在创建
return proxyClassCache.get(loader, interfaces);
}
忽略相关细节,直接来看如何创建代理类的:
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
// 如果目标类实现的接口数大于65535个则抛出异常,真的会存在这么多的接口吗?
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 声明代表proxy类的class对象
Class proxyClass = null;
String[] interfaceNames = new String[interfaces.length];
Set interfaceSet = new HashSet(); // for detecting duplicates
// 遍历目标类所实现的接口,并将其写入到内存中
for (int i = 0; i < interfaces.length; i++) {
// 获得需要代理类所实现的接口的名称
String interfaceName = interfaces[i].getName();
Class interfaceClass = null;
try {
// 加载需要代理类实现的接口到内存中
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
//省略一些无关紧要的代码 .......
// 把目标类实现的接口代表的Class对象放到Set中
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
// 把目标类实现的接口名称作为缓存(Map)中的key
Object key = Arrays.asList(interfaceNames);
Map cache;
synchronized (loaderToCache) {
// 从缓存中获取cache
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
// 如果获取不到,则new个HashMap实例
cache = new HashMap();
// 把HashMap实例和当前加载器放到缓存中
loaderToCache.put(loader, cache);
}
}
synchronized (cache) {
do {
// 根据接口的名称从缓存中获取对象
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
if (proxyClass != null) {
// 如果代理对象的Class实例已经存在,则直接返回
return proxyClass;
} else if (value == pendingGenerationMarker) {
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
try {
// 中间省略部分代码 .......
// 这里就是动态生成代理对象的最关键的地方
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
// 根据代理类的字节码生成代理类的实例
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);
}
// 中间省略了一些代码 .......
return proxyClass;
}
从中可以看出,它遍历了传入的所有接口并获取了它们的名称,接下来就是利用字节码操作生成相关的class字节码,然后看看获取字节码的generateProxyClass类:
public static byte[] generateProxyClass(final String name,
Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 这里动态生成代理类的字节码,涉及字节码操作,目前还看不了
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
// 返回代理类的字节码
return classFile;
}
上面的两个类可以说明JDK是如何生成的代理类的字节码,现在需要明白的就是,它们之间的关系是如何建立的。这个就需要查看代理类的源代码,直接利用创建字节码类来生成其.class文件,然后反编译得到源代码。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true)
源代码如下:
public final class $Proxy11 extends Proxy implements ShouldDoSomeThing {
// 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例
public $Proxy11(InvocationHandler invocationhandler) {
super(invocationhandler);
}
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
.booleanValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
/*
* 实现了相关的接口
*/
@Override
public void doIt(String str) {
try {
// 从这里可以看出,在代理类内部所实现的需要实现的接口是调用了
// 与之相关联的handler对象的invoke方法,那handler对象
// 哪里来的?还记得在newInstance的时候传入的pc吗?它就来
// 自那里!
super.h.invoke(this, m3, null);
return;
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
// 这里利用反射,获得了代理类其中关键的是m3这个方法,而且m3这个方法还是委 // 托类的,同时这个m3作为参数,传递给了handler的invoke方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("dp.ShouldDoSomeThing").getMethod(
"ShouldDoSomeThing", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
到目前为止,整个原理都已经解释,其中一些和思想无关的代码并没有描述。JDK的动态代理使用了字节码操作生成代理类的字节码,使用反射来执行代理,现在再看上面的图如果能够感觉到思路清晰,那么动态代理的思想也就基本OK了,但是JDK动态代理只能是代理接口,并不能代理类,CGLIB 是弥补了这一缺点。
由于最近需要搭建一个基础的Web框架,所以就看了下动态代理在AOP方面的运用,当然,代理不仅仅是AOP方面才有运用,代理是对委托对象访问的一种控制,在许多方面都有用,“设计”这种东西有时候真心很费脑细胞,实现反而更加简单。
JDK1.8对Proxy的改进相当大,由于我windows的JDK是1.8,那个真心复杂了很多!不利于对动态代理整体的掌握,所以本文参考了http://rejoy.iteye.com/blog/1627405#comments的部分代码并有部分改变。
再说下那个代理类名字的问题:$Proxy是所有代理类的名字前缀,其后N代表一个数字,每次在map中新增一个class对象的时候都会使数字自增1,,所以代理类的名字可能是这样的----$Proxy20
薄言有误,还请指出一起学习改进。