写一些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. * @author haitao.yao
  81. * Dec 14, 2010
  82. */
  83. static class TestBean implements Serializable{
  84. /**
  85. *
  86. */
  87. private static final long serialVersionUID = -5994966479456252766L;
  88. private String name;
  89. private int age;
  90. /**
  91. * @return the name
  92. */
  93. public String getName() {
  94. return name;
  95. }
  96. /**
  97. * @param name the name to set
  98. */
  99. public void setName(String name) {
  100. this.name = name;
  101. }
  102. /**
  103. * @return the age
  104. */
  105. public int getAge() {
  106. return age;
  107. }
  108. /**
  109. * @param age the age to set
  110. */
  111. public void setAge(int age) {
  112. this.age = age;
  113. }
  114. }
  115. }

import java.io.Serializable;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
/**
* @author haitao.yao Dec 14, 2010
*/
public class ReflectionCompare {
private static final int count = 10000000;
/**
* @param args
*/
public static void main(String[] args) {
long duration = testIntCommon();
System.out.println("int common test for " + count
+ " times, duration: " + duration);
duration = testUnsafe();
System.out.println("int unsafe test for " + count
+ " times, duration: " + duration);
}
private static long testUnsafe() {
long start = System.currentTimeMillis();
sun.misc.Unsafe unsafe = getUnsafe();
int temp = count;
Field field = getIntField();
long offset = unsafe.objectFieldOffset(field);
while (temp-- > 0) {
unsafe.getInt(new TestBean(), offset);
}
return System.currentTimeMillis() - start;
}
private static long testIntCommon() {
long start = System.currentTimeMillis();
int temp = count;
getIntField().setAccessible(true);
while (temp-- > 0) {
TestBean bean = new TestBean();
try {
getIntField().get(bean);
} catch (Exception e) {
e.printStackTrace();
}
}
return System.currentTimeMillis() - start;
}
private static final sun.misc.Unsafe unsafe;
static {
sun.misc.Unsafe value = null;
try {
Class<?> clazz = Class.forName("sun.misc.Unsafe");
Field field = clazz.getDeclaredField("theUnsafe");
field.setAccessible(true);
value = (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("error to get theUnsafe", e);
}
unsafe = value;
}
public static final sun.misc.Unsafe getUnsafe() {
return unsafe;
}
private static final Field intField;
private static final Field stringField;
static {
try {
intField = TestBean.class.getDeclaredField("age");
stringField = TestBean.class.getDeclaredField("name");
} catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException("failed to init testbean field", e);
}
}
public static final Field getIntField() {
return intField;
}
public static final Field getStringField() {
return stringField;
}

/**
* @author haitao.yao
* Dec 14, 2010
*/
static class TestBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5994966479456252766L;

private String name;
private int age;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
}
}

通过测试发现,效率是普通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

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

Java 反射调用的一种优化的更多相关文章

  1. Java 反射 调用私有域和方法(setAccessible)

    Java 反射 调用私有域和方法(setAccessible) @author ixenos AccessibleObject类 Method.Field和Constructor类共同继承了Acces ...

  2. 利用java反射调用类的的私有方法--转

    原文:http://blog.csdn.net/woshinia/article/details/11766567 1,今天和一位朋友谈到父类私有方法的调用问题,本来以为利用反射很轻松就可以实现,因为 ...

  3. JAVA反射调用方法

    1.用户类 package com.lf.entity; import com.lf.annotation.SetProperty; import com.lf.annotation.SetTable ...

  4. 通过Java反射调用方法

    这是个测试用的例子,通过反射调用对象的方法.     TestRef.java import java.lang.reflect.Method; import java.lang.reflect.In ...

  5. java反射调用dubbo接口

    需求:项目增加幂等 场景:1.三个项目:a .b.c2.a项目加幂等3.b项目dubbo调用项目a的时候超时没有获取返回结果,增加重试机制(非立即重试,3min or 5min 后重试)4.c项目是一 ...

  6. Java 反射 调用私有构造方法

    单例类: package singleton; public class SingletonTest { // 私有构造方法 private SingletonTest(){ System.out.p ...

  7. java黑魔法-反射机制-02-通过Java反射调用其他类方法

    package com.aaron.reflect; import java.lang.reflect.Method; import java.lang.reflect.InvocationTarge ...

  8. java反射调用api

    cglib的fastmethod 简单示例: FastClass serviceFastClass = FastClass.create(Person.class); Person p = new P ...

  9. Java 反射调用方法 - 不跳过安全检查、跳过安全检查和普通方法性能比较测试

    java中反射提供灵活性同时,给运行效率带来了一定影响.写个代码测试一下 package com.xzlf.reflectTest; import java.lang.reflect.Method; ...

随机推荐

  1. loj2540 「PKUWC 2018」随机算法

    pkusc 快到了--做点题涨涨 rp. 记 \(f(S,i)\) 表示 \(S\) 这个集合是决计不能选的(要么属于独立集,要么和独立集相连),或称已经考虑了的,\(i\) 表示此集合对应的最大独立 ...

  2. 如何使用Eclipse调试framework

    1.下载Eclipse EE(下载地址:http://www.eclipse.org/downloads/) 2.下载并安装JDK(下载地址:http://www.oracle.com/technet ...

  3. 自定义toolbar教程

    1.写toolbar的布局文件 ,toolbar.xml <?xml version="1.0" encoding="utf-8"?> <Re ...

  4. C++中的垃圾回收和内存管理

    最开始的时候看到了许式伟的内存管理变革系列,看到性能测试结果的时候,觉得这个实现很不错,没有深入研究其实现.现在想把这个用到自己的一个项目中来,在linux下编译存在一些问题,所以打算深入研究一下. ...

  5. bzoj 3224 裸平衡树

    裸的平衡树,可以熟悉模板用,写题写不出来的时候可以A以下缓解下心情. /************************************************************** P ...

  6. Linux 内核链表的使用及深入分析【转】

    转自:http://blog.csdn.net/BoArmy/article/details/8652776 1.内核链表和普通链表的区别 内核链表是一个双向链表,但是与普通的双向链表又有所区别.内核 ...

  7. IC卡的传输协议(2)-块传输协议T=1续【转】

    转自:http://bbs.ednchina.com/BLOG_ARTICLE_172025.HTM (3)容错操作 先来看一下容错的规则定义. * 复位应答后,第一个数据块是由终端发往IC卡的,而且 ...

  8. (十二)Linux内核驱动之poll和select

    使用非阻塞 I/O 的应用程序常常使用 poll, select, 每个允许一个进程来决定它是否可读或者写一个或多个文件而不阻塞. 这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写.  ...

  9. <asp:TextBox><asp:LinkButton><input button>调用后台方法后刷新页面

    <asp:TextBox><asp:LinkButton>服务器控件,执行后台方法,会回调加载js,相当于页面重新加载,刷新页面 <input button>不能直 ...

  10. 如何生成pyc/pyo/pyd文件

    # 一.如何生成pyc/pyo文件 # 1.通过编写代码生成 import py_compile # 参数如下 ''' def compile(file, cfile=None, dfile=None ...