[编织消息框架][JAVA核心技术]jdk动态代理

需要用到的工具  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 com\eyu\onequeue\TestProxy.class > s.txt

其中

 // Method java/lang/reflect/Proxy.newProxyInstance:(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;

没有详细看到代理类指令

运行时生成的动态代理对象是可以导出到文件的,方法有两种

  1. 在代码中加入System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  2. 在运行时加入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

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:\java\findme\com\sun\proxy>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动态代理比原生调用只慢几十毫秒,这点可以忽略不计

 

posted @ 2017-03-27 16:20  solq321  阅读(376)  评论(0编辑  收藏  举报