众所周知Java的动态代理由Proxy和InvocationHander实现。以下代码演示了简单地应用:
public interface IFoo { public void load(int i); public void save(Object o); public List<Object> list(); }
public class Foo implements IFoo { public void load(int i) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("load() is running"); } public void save(Object o) { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("save() is running"); } public List<Object> list() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("list() is running"); return null; } }
用动态代理实现计算运行时间和打印日志。
public class MyProxy { public static void main(String[] args) { time(); log(); } static void time() { final Foo foo = new Foo(); IFoo myFoo = (IFoo) Proxy.newProxyInstance( MyProxy.class.getClassLoader(), foo.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = new Date().getTime(); Object rtn = method.invoke(foo, args); long end = new Date().getTime(); System.out.println(method.getName() + " method takes " + TimeUnit.MILLISECONDS.toSeconds(end - start) + " seconds"); return rtn; } }); System.out.println(myFoo.getClass()); myFoo.list(); myFoo.load(5); myFoo.save(""); } static void log() { final Foo foo = new Foo(); IFoo myFoo = (IFoo) Proxy.newProxyInstance( MyProxy.class.getClassLoader(), foo.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object rtn = method.invoke(foo, args); System.out.println(method.getName() + "(" + (args == null ? "" : args) + ") runs at " + new Date()); return rtn; } }); System.out.println(myFoo.getClass()); myFoo.list(); myFoo.load(5); myFoo.save(""); } }
运行结果如下:
class com.sun.proxy.$Proxy0
list() is running
list method takes 3 seconds
load() is running
load method takes 1 seconds
save() is running
save method takes 2 seconds
class com.sun.proxy.$Proxy0
list() is running
list() runs at Fri Mar 28 17:52:13 CST 2014
load() is running
load([Ljava.lang.Object;@961dff) runs at Fri Mar 28 17:52:14 CST 2014
save() is running
save([Ljava.lang.Object;@18b81e3) runs at Fri Mar 28 17:52:16 CST 2014
可以看到动态生成了类型$Proxy0的对象即完成计时和日志功能的代理对象。而这里最核心的部分毫无疑问是newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),这个方法返回一个实例,该实例继承了Proxy类并实现了IFoo接口,并接受一个InvocationHandler作为成员变量,而每一个方法的具体实现实际上调用的都是实现InvocationHandler接口的匿名内部类对象的invoke(Object proxy, Method method,Object[] args) 方法,在该方法内部通过反射延续了被代理对象的行为。
接下来,我们通过查看一些源码片段来做更深入地了解:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { if (h == null) { throw new NullPointerException(); } final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, interfaces); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, interfaces); /* * Invoke its constructor with the designated invocation handler. */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
这里的核心是Class<?> cl = getProxyClass0(loader, interfaces),而后面的部分是通过反射实例化一个代理对象。
选取部分代码来解读生成代理类Class的大概步骤,首先是定义class name。它由三部分组成proxyPkg + proxyClassNamePrefix + num。package name,如果没有非公共接口即采用com.sun.proxy作为包名加上$Proxy和自增长的数字,这也就是上文运行结果中看到的com.sun.proxy.$Proxy0。而接下来才是更加核心的部分生成符合JVM规范的Class字节码。
try { String proxyPkg = null; // package to define proxy class in for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf(‘.‘); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } { long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } proxyClasses.put(proxyClass, null); }
通过以下代码片段可以看出它按照JVM规范输出字节码,魔数、常量池、访问标志、类索引等等
// u4 magic; dout.writeInt(0xCAFEBABE); // u2 minor_version; dout.writeShort(CLASSFILE_MINOR_VERSION); // u2 major_version; dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags; dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // u2 this_class; dout.writeShort(cp.getClass(dotToSlash(className))); // u2 super_class; dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count;
而下面这段代码也部分印证了代理对象的方法实际上是对InvocationHandler的invoke方法的调用。
out.writeShort(cp.getInterfaceMethodRef( "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + "[Ljava/lang/Object;)Ljava/lang/Object;"));
但从这些字节码我们似乎还很难一睹这个代理类的全貌,为了一睹为快,我尝试借助Instrumentaion来获取内存中的这个Class对象(当然我们可以通过修改JDK源码)。
public class MyTransformer implements ClassFileTransformer { public static void premain(String options, Instrumentation ins) { ins.addTransformer(new MyTransformer()); } public byte[] transform(ClassLoader loader, String className, Class cBR, java.security.ProtectionDomain pD, byte[] classfileBuffer) throws IllegalClassFormatException { if (className.contains("com/sun/proxy/$Proxy0")) { try { FileChannel fc = new FileOutputStream("D:\\$Proxy0.class") .getChannel(); fc.write(ByteBuffer.wrap(classfileBuffer)); fc.close(); } catch (IOException e) { e.printStackTrace(); } } return classfileBuffer; } }
然后打包成Jar,设置Premain-Class,指定javaagent运行,得到class文件,然后反编译,end of story
package com.sun.proxy; import IFoo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import java.util.List; public final class $Proxy0 extends Proxy implements IFoo { private static Method m1; private static Method m5; private static Method m0; private static Method m3; private static Method m4; private static Method m2; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void save(Object paramObject) throws { try { this.h.invoke(this, m5, new Object[] { paramObject }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void load(int paramInt) throws { try { this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final List list() throws { try { return (List)this.h.invoke(this, m4, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m5 = Class.forName("IFoo").getMethod("save", new Class[] { Class.forName("java.lang.Object") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m3 = Class.forName("IFoo").getMethod("load", new Class[] { Integer.TYPE }); m4 = Class.forName("IFoo").getMethod("list", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
本文出自 “那个蒙奇” 博客,请务必保留此出处http://thatmonkey.blog.51cto.com/7935609/1387491