• Java 反射调用的一种优化


    写一些Java框架的时候,经常需要通过反射get或者set某个bean的field,比较普通的做法是获取field后调用java.lang.reflect.Field.get(Object),但每次都这样调用,能否有优化的空间呢?

    答案是有。

    第一种:

          由于每次都是重复的调用,所以想到了缓存每个bean的field,但这样做还是不够,所以想到了写一个code generator。通过生成代码的方式,get或者set每个bean的时候直接调用该bean的getter或者setter,这个实现听起来很牛逼,其实就是用asm生成一个类在用一个classloader加载进来每次调用直接invoke就可以了。

          可单纯为了一个反射调用做这么多,总感觉是大炮打了蚊子。

    第二种:

          多谢@RednaxelaFX   的指点,找到了更简单的做法:sun.misc.Unsafe

          使用也非常的简单:首先通过sun.misc.Unsafe.objectFieldOffset(Field) 获取field的offset,然后使用sun.misc.Unsafe.getObject(Object, long)获取某个实例上的field的值。

          简单的测试代码如下:

    1. import java.io.Serializable;  
    2. import java.lang.reflect.Field;  
    3. import sun.misc.Unsafe;  
    4. /** 
    5.  * @author haitao.yao Dec 14, 2010 
    6.  */  
    7. public class ReflectionCompare {  
    8.     private static final int count = 10000000;  
    9.     /** 
    10.      * @param args 
    11.      */  
    12.     public static void main(String[] args) {  
    13.         long duration = testIntCommon();  
    14.         System.out.println("int common test for  " + count  
    15.                 + " times, duration: " + duration);  
    16.         duration = testUnsafe();  
    17.         System.out.println("int unsafe test for  " + count  
    18.                 + " times, duration: " + duration);  
    19.     }  
    20.     private static long testUnsafe() {  
    21.         long start = System.currentTimeMillis();  
    22.         sun.misc.Unsafe unsafe = getUnsafe();  
    23.         int temp = count;  
    24.         Field field = getIntField();  
    25.         long offset = unsafe.objectFieldOffset(field);  
    26.         while (temp-- > 0) {  
    27.             unsafe.getInt(new TestBean(), offset);  
    28.         }  
    29.         return System.currentTimeMillis() - start;  
    30.     }  
    31.     private static long testIntCommon() {  
    32.         long start = System.currentTimeMillis();  
    33.         int temp = count;  
    34.         getIntField().setAccessible(true);  
    35.         while (temp-- > 0) {  
    36.             TestBean bean = new TestBean();  
    37.             try {  
    38.                 getIntField().get(bean);  
    39.             } catch (Exception e) {  
    40.                 e.printStackTrace();  
    41.             }  
    42.         }  
    43.         return System.currentTimeMillis() - start;  
    44.     }  
    45.     private static final sun.misc.Unsafe unsafe;  
    46.     static {  
    47.         sun.misc.Unsafe value = null;  
    48.         try {  
    49.             Class<?> clazz = Class.forName("sun.misc.Unsafe");  
    50.             Field field = clazz.getDeclaredField("theUnsafe");  
    51.             field.setAccessible(true);  
    52.             value = (Unsafe) field.get(null);  
    53.         } catch (Exception e) {  
    54.             e.printStackTrace();  
    55.             throw new RuntimeException("error to get theUnsafe", e);  
    56.         }  
    57.         unsafe = value;  
    58.     }  
    59.     public static final sun.misc.Unsafe getUnsafe() {  
    60.         return unsafe;  
    61.     }  
    62.     private static final Field intField;  
    63.     private static final Field stringField;  
    64.     static {  
    65.         try {  
    66.             intField = TestBean.class.getDeclaredField("age");  
    67.             stringField = TestBean.class.getDeclaredField("name");  
    68.         } catch (Exception e) {  
    69.             e.printStackTrace();  
    70.             throw new IllegalStateException("failed to init testbean field", e);  
    71.         }  
    72.     }  
    73.     public static final Field getIntField() {  
    74.         return intField;  
    75.     }  
    76.     public static final Field getStringField() {  
    77.         return stringField;  
    78.     }  
    79.       
    80.     /** 
    81.      * @author haitao.yao 
    82.      * Dec 14, 2010 
    83.      */  
    84.     static class TestBean implements Serializable{  
    85.         /** 
    86.          *  
    87.          */  
    88.         private static final long serialVersionUID = -5994966479456252766L;  
    89.           
    90.         private String name;  
    91.         private int age;  
    92.         /** 
    93.          * @return the name 
    94.          */  
    95.         public String getName() {  
    96.             return name;  
    97.         }  
    98.         /** 
    99.          * @param name the name to set 
    100.          */  
    101.         public void setName(String name) {  
    102.             this.name = name;  
    103.         }  
    104.         /** 
    105.          * @return the age 
    106.          */  
    107.         public int getAge() {  
    108.             return age;  
    109.         }  
    110.         /** 
    111.          * @param age the age to set 
    112.          */  
    113.         public void setAge(int age) {  
    114.             this.age = age;  
    115.         }  
    116.     }  
    117. }  

          通过测试发现,效率是普通java.lang.reflect.Field.get(Object)的3倍,当然,性能这个东西,还是自己测试了放心。

          这样做有一个不好的地方:sun.misc.Unsafe在sun的包里,默认情况下,eclipse编译会报错,在Window->Preference->Java->Compiler->Errors/Warnings->Deprecated and restricted API -> Forbidden Reference 修改成Warning或者Ignore就可以了。由于Unsafe在JDK中很多的类库中都在使用,框架代码中使用还是很安全的,如果需要api变动,JDK源代码的修改工作量比我们的大多了 :-0

          至于第一种方法,虽然麻烦,有时间还是可以尝试一下的,有时间了写一下。

  • 相关阅读:
    BootStrap 模态框禁用空白处点击关闭
    【云计算】使用nsenter进入Docker容器进行调试
    【架构】Nginx如何设置X-Request-ID请求头,记录请求时间:毫秒?
    【架构】微服务系列文章
    【云计算】OpenStack项目全面介绍
    【云计算】CloudFoundry参考资料
    【云计算】OpenShift容器服务参考
    【Web】Django OAuth invalid_grant error
    【Linux】Shell三类变量的作用域——linux shell “永久环境变量”、“临时环境变量”和"普通变量"之完全解读
    【云计算】OpenStack Horizon DashBoard定制化,完整实现前后台交互
  • 原文地址:https://www.cnblogs.com/heartstage/p/3363908.html
Copyright © 2020-2023  润新知