需要用到的工具 jdk : javac javap
class 反编译 :JD-GUI http://jd.benow.ca/
先来看下jdk动态代理跟native性能比较
1 package com.eyu.onequeue; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 public class TestProxy { 8 public interface UserService { 9 public String getName(int id); 10 11 public Integer getAge(int id); 12 } 13 14 public static class UserServiceImpl implements UserService { 15 16 @Override 17 public String getName(int id) { 18 return "name : " + id; 19 } 20 21 @Override 22 public Integer getAge(int id) { 23 return id; 24 } 25 }; 26 27 public static void main(String[] args) { 28 testNative(); 29 testJdk(); 30 } 31 32 public static void testJdk() { 33 UserService impTarget = new UserServiceImpl(); 34 // 代理处理逻辑 35 InvocationHandler handler = new InvocationHandler() { 36 37 @Override 38 public Object invoke(Object target, Method method, Object[] args) throws Throwable { 39 return method.invoke(impTarget, args); 40 } 41 }; 42 // Proxy.newProxyInstance(ClassLoader/**ClassLoader 没有特别处理 拿默认即可 **/, 43 // Class<?>[]/**代理接口类**/, InvocationHandler /**代理处理逻辑**/) 44 UserService proxy = (UserService) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), new Class[] { UserService.class }, handler); 45 46 run("jdk", proxy); 47 } 48 49 public static void testNative() { 50 UserService impTarget = new UserServiceImpl(); 51 run("native", impTarget); 52 } 53 54 private static void run(String tag, UserService impTarget) { 55 int c = 15; 56 System.out.println(); 57 while (c-- > 0) { 58 long start = System.currentTimeMillis(); 59 for (int i = 0; i < 10000000; i++) { 60 impTarget.getName(11); 61 } 62 long end = System.currentTimeMillis(); 63 System.out.print(tag + ": " + (end - start) + " "); 64 } 65 } 66 }
运行结果:
native: 175 native: 182 native: 126 native: 172 native: 126 native: 127 native: 127 native: 126 native: 127 native: 126 native: 126 native: 128 native: 126 native: 127 native: 126
jdk: 214 jdk: 170 jdk: 169 jdk: 169 jdk: 170 jdk: 170 jdk: 170 jdk: 170 jdk: 170 jdk: 172 jdk: 169 jdk: 172 jdk: 169 jdk: 171 jdk: 169
先运行预热,看出执行五次之后比较稳定
jdk动态代理使用非常简单,使用Proxy.newProxyInstance 静态方法即可
接下来我们看下class指令
javac -encoding UTF-8 -d . TestProxy.java
javap -v comeyuonequeueTestProxy.class > s.txt
其中
// Method java/lang/reflect/Proxy.newProxyInstance:(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;
没有详细看到代理类指令
运行时生成的动态代理对象是可以导出到文件的,方法有两种
- 在代码中加入
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
- 在运行时加入jvm 参数
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
我们在main方法加一行System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 然后执行一下
1 public static void main(String[] args) { 2 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 3 //省略 4 }
这时在包下会多出$Proxy0.class文件
proxy0.class用jd-gui打开
1 package com.sun.proxy; 2 3 import com.eyu.onequeue.TestProxy.UserService; 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.Method; 6 import java.lang.reflect.Proxy; 7 import java.lang.reflect.UndeclaredThrowableException; 8 9 public final class $Proxy0 10 extends Proxy 11 implements TestProxy.UserService 12 { 13 private static Method m1; 14 private static Method m2; 15 private static Method m3; 16 private static Method m4; 17 private static Method m0; 18 19 public $Proxy0(InvocationHandler paramInvocationHandler) 20 { 21 super(paramInvocationHandler); 22 } 23 24 public final boolean equals(Object paramObject) 25 { 26 try 27 { 28 return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); 29 } 30 catch (Error|RuntimeException localError) 31 { 32 throw localError; 33 } 34 catch (Throwable localThrowable) 35 { 36 throw new UndeclaredThrowableException(localThrowable); 37 } 38 } 39 40 public final String toString() 41 { 42 try 43 { 44 return (String)this.h.invoke(this, m2, null); 45 } 46 catch (Error|RuntimeException localError) 47 { 48 throw localError; 49 } 50 catch (Throwable localThrowable) 51 { 52 throw new UndeclaredThrowableException(localThrowable); 53 } 54 } 55 56 public final String getName(int paramInt) 57 { 58 try 59 { 60 return (String)this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) }); 61 } 62 catch (Error|RuntimeException localError) 63 { 64 throw localError; 65 } 66 catch (Throwable localThrowable) 67 { 68 throw new UndeclaredThrowableException(localThrowable); 69 } 70 } 71 72 public final Integer getAge(int paramInt) 73 { 74 try 75 { 76 return (Integer)this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt) }); 77 } 78 catch (Error|RuntimeException localError) 79 { 80 throw localError; 81 } 82 catch (Throwable localThrowable) 83 { 84 throw new UndeclaredThrowableException(localThrowable); 85 } 86 } 87 88 public final int hashCode() 89 { 90 try 91 { 92 return ((Integer)this.h.invoke(this, m0, null)).intValue(); 93 } 94 catch (Error|RuntimeException localError) 95 { 96 throw localError; 97 } 98 catch (Throwable localThrowable) 99 { 100 throw new UndeclaredThrowableException(localThrowable); 101 } 102 } 103 104 static 105 { 106 try 107 { 108 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); 109 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 110 m3 = Class.forName("com.eyu.onequeue.TestProxy$UserService").getMethod("getName", new Class[] { Integer.TYPE }); 111 m4 = Class.forName("com.eyu.onequeue.TestProxy$UserService").getMethod("getAge", new Class[] { Integer.TYPE }); 112 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 113 return; 114 } 115 catch (NoSuchMethodException localNoSuchMethodException) 116 { 117 throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); 118 } 119 catch (ClassNotFoundException localClassNotFoundException) 120 { 121 throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); 122 } 123 } 124 }
proxy0分析分两部份
1.在内存动态生成代理类 以$proxy 开头
$Proxy0 extends Proxy implements XXXXProxy.UserService
并初始化InvocationHandler 同绑定 Method
1 public $Proxy0(InvocationHandler paramInvocationHandler) 2 { 3 super(paramInvocationHandler); 4 } 5 6 static 7 { 8 try 9 { 10 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); 11 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 12 m3 = Class.forName("com.eyu.onequeue.TestProxy$UserService").getMethod("getName", new Class[] { Integer.TYPE }); 13 m4 = Class.forName("com.eyu.onequeue.TestProxy$UserService").getMethod("getAge", new Class[] { Integer.TYPE }); 14 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 15 return; 16 } 17 ..... 18 }
第二部分:代理原对象所有方法实现调用InvocationHandler 类的 Object invoke(Object target, Method method, Object[] args) throws Throwable 方法 再通过method反射invoke
public final String getName(int paramInt) { try { return (String)this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }
我们通过生成指令 E:javafindmecomsunproxy>javap -v $Proxy0.class > d.txt
来查看代理过的getName方法共多少条指令
public final java.lang.String getName(int) throws ; descriptor: (I)Ljava/lang/String; flags: ACC_PUBLIC, ACC_FINAL Code: stack=10, locals=3, args_size=2 0: aload_0 1: getfield #16 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler; 4: aload_0 5: getstatic #57 // Field m3:Ljava/lang/reflect/Method; 8: iconst_1 9: anewarray #22 // class java/lang/Object 12: dup 13: iconst_0 14: iload_1 15: invokestatic #63 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 18: aastore 19: invokeinterface #28, 4 // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; 24: checkcast #52 // class java/lang/String 27: areturn 28: athrow 29: astore_2 30: new #42 // class java/lang/reflect/UndeclaredThrowableException 33: dup 34: aload_2 35: invokespecial #45 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V 38: athrow Exception table: from to target type 0 28 28 Class java/lang/Error 0 28 28 Class java/lang/RuntimeException 0 28 29 Class java/lang/Throwable Exceptions: throws
如果不出错,到 27:areturn 至少要执行到13条指令 java8对动态代理有优化过
结论是:jdk动态代理比原生调用只慢几十毫秒,这点可以忽略不计