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


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