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的值。

      简单的测试代码如下:

 

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

 

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

给TA打赏
共{{data.count}}人
人已打赏
安全技术

网站制作需要素材的实用网站

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索