Java动态代理机制一窥

       众所周知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 storyj_0057.gif


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());
    }
  }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章