我们经常会用到Java的动态代理技术, 虽然会使用, 但是自己对其中的原理却不是很了解.比如代理对象是如何产生的, InvocationHandler的invoke方法是如何调用的?今天就来深究下Java动态代理的原理.
1.动态代理的使用
动态代理两个最关键的类,是InvocationHandler和Proxy. 下面看下如何使用动态代理, 代码如下:
package proxy; /** * Created by xinfengyao on 16-12-20.
被代理的类实现的接口 */ public interface Interface { void getName(); String getNameById(String id); }
package proxy; /** * Created by xinfengyao on 16-12-20.
被代理的类 */ public class RealObject implements Interface { @Override public void getName() { System.out.println("my name is xinfeng.yao"); } @Override public String getNameById(String id) { System.out.println("argument id: " + id); return "xinfeng.yao"; } }
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; /** * Created by xinfengyao on 16-12-20.
实现自己的InvocationHandler */ public class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("dynamic proxy name: " + proxy.getClass()); System.out.println("method: " + method.getName()); System.out.println("args: " + Arrays.toString(args)); Object invokeObject = method.invoke(proxied, args); if (invokeObject != null) { System.out.println("invoke object: " + invokeObject.getClass()); } else { System.out.println("invoke object is null"); } return invokeObject; } }
下面看下测试代码:
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Created by xinfengyao on 16-12-20. */ public class DynamicProxyMain { public static void consumer(Interface iface) { iface.getName(); String name = iface.getNameById("1"); System.out.println("name: " + name); } public static void main(String[] args) { RealObject realObject = new RealObject(); consumer(realObject); System.out.println("====================="); ClassLoader classLoader = Interface.class.getClassLoader(); Class<?>[] interfaces = new Class[]{Interface.class}; InvocationHandler handler = new DynamicProxyHandler(realObject); Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler); System.out.println("in dynamicProxyMain proxy: " + proxy.getClass()); consumer(proxy); } }
运行结果:
my name is xinfeng.yao argument id: 1 name: xinfeng.yao ===================== in dynamicProxyMain proxy: class com.sun.proxy.$Proxy0 dynamic proxy name: class com.sun.proxy.$Proxy0 method: getName args: null my name is xinfeng.yao invoke object is null dynamic proxy name: class com.sun.proxy.$Proxy0 method: getNameById args: [1] argument id: 1 invoke object: class java.lang.String name: xinfeng.yao
用起来其实很简单.但是如果要是能搞清楚背后的原理再好不过了.
2.动态代理原理
我们在使用动态代理时, 调用Proxy.newProxyInstance()方法产生代理对象, 那么我们看看这方法都干了什么. 下面这段代码就直接复制别人的了.
- **
- * loader:类加载器
- * interfaces:目标对象实现的接口
- * h:InvocationHandler的实现类
- */
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- if (h == null) {
- throw new NullPointerException();
- }
- /*
- * Look up or generate the designated proxy class.
- */
- Class cl = getProxyClass(loader, interfaces);
- /*
- * Invoke its constructor with the designated invocation handler.
- */
- try {
- // 调用代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
- Constructor cons = cl.getConstructor(constructorParams);
- // 生成代理类的实例并把MyInvocationHandler的实例传给它的构造方法
- return (Object) cons.newInstance(new Object[] { h });
- } catch (NoSuchMethodException e) {
- throw new InternalError(e.toString());
- } catch (IllegalAccessException e) {
- throw new InternalError(e.toString());
- } catch (InstantiationException e) {
- throw new InternalError(e.toString());
- } catch (InvocationTargetException e) {
- throw new InternalError(e.toString());
- }
- }
我们再进去getProxyClass方法看一下
- public static Class<?> getProxyClass(ClassLoader loader,
- Class<?>... interfaces)
- throws IllegalArgumentException
- {
- // 如果目标类实现的接口数大于65535个则抛出异常(我XX,谁会写这么NB的代码啊?)
- if (interfaces.length > 65535) {
- throw new IllegalArgumentException("interface limit exceeded");
- }
- // 声明代理对象所代表的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) {
- // 如果获取不到,则新建地个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;
- }
进去ProxyGenerator类的静态方法generateProxyClass,这里是真正生成代理类class字节码的地方。
- 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是怎样动态生成代理类的字节的原理已经一目了然了。
总结下,其实就是我们在调用下面这个方法时, jvm在内部帮我们动态生成了一个class字节码文件, 这个class字节码文件对应的就是下面这个方法返回的类.
Proxy.newProxyInstance(classLoader, interfaces, handler)
我们得到的代理类Proxy, 其中一个构造函数传入了我们的调用
Proxy.newProxyInstance(classLoader, interfaces, handler)
方法时传入的handler, 代理类调用具体方法的时候, 其实是调用handler的invoke方法.这就是动态代理的原理.
可能说的不够明白, 下面我们通过一定的方法自己来产生这个代理类的class文件, 然后在idea中打开这个文件就非常清楚了.
代码如下:
package proxy; import sun.misc.ProxyGenerator; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * Created by xinfengyao on 16-12-20. */ public class ProxyGeneratorUtils { public static void writeProxyClassToHardDisk(String path) { byte[] proxyClassFile = ProxyGenerator.generateProxyClass("aabbcc", new Class[]{Interface.class}); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(proxyClassFile); out.flush(); System.out.println("end1"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { out.close(); System.out.println("end2"); } catch (Exception e) { e.printStackTrace(); } } } }
上面这个方法就是用来产生字节码文件的, path是你需要保存字节码文件的位置, 代码中的aabbcc是我随便给这个类取得一个名字. Interface是这个代理类需要实现的接口,就是上面代码给出的Interface.
看看测试代码:
package proxy; /** * Created by xinfengyao on 16-12-20. */ public class ProxyTest { public static void main(String[] args) { ProxyGeneratorUtils.writeProxyClassToHardDisk("/home/xinfengyao/mustang/projects/train/pattern/target/classes/proxy/myProxy.class"); } }
运行后, 发现在相应的位置产生了我们想要的class文件, 如图:
我们直接打开这个class文件, 看看jvm给我们产生的这个class文件对应的源码是什么
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import proxy.Interface; public final class aabbcc extends Proxy implements Interface { private static Method m3; private static Method m4; private static Method m1; private static Method m0; private static Method m2;
// 看, 这里传入了我们实现的InvocationHandler public aabbcc(InvocationHandler var1) throws { super(var1); }
// 看, 有没有很熟悉,这个方法就是我们接口中定义的一个方法 public final void getName() throws { try {
// 这行代码就是调用我们实现的InvocationHandler的invoke方法, 终于明白了吧 super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
// 看, 这个方法也是我们接口中定义的一个方法 public final String getNameById(String var1) throws { try { return (String)super.h.invoke(this, m4, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final boolean equals(Object var1) throws { try { 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 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); } } 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); } } static { try {
// 这两个方法就是我们接口中定义的 m3 = Class.forName("proxy.Interface").getMethod("getName", new Class[0]); m4 = Class.forName("proxy.Interface").getMethod("getNameById", new Class[]{Class.forName("java.lang.String")});
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
通过上面源码, 我们可以清楚地看到, 我们调用
Proxy.newProxyInstance(classLoader, interfaces, handler)这个方法时,传入的handler确实是作为代理类构造函数的一个参数传进去了
我们还看到动态产生的代理类, 实现了我们的接口, 在实现的方法中, 调用了我们InvocationHandler中的invoke()方法.这就是动态代理的原理.
参考博文:http://rejoy.iteye.com/blog/1627405