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;
- }
- }
- }
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 反射调用的一种优化的更多相关文章
- Java 反射 调用私有域和方法(setAccessible)
Java 反射 调用私有域和方法(setAccessible) @author ixenos AccessibleObject类 Method.Field和Constructor类共同继承了Acces ...
- 利用java反射调用类的的私有方法--转
原文:http://blog.csdn.net/woshinia/article/details/11766567 1,今天和一位朋友谈到父类私有方法的调用问题,本来以为利用反射很轻松就可以实现,因为 ...
- JAVA反射调用方法
1.用户类 package com.lf.entity; import com.lf.annotation.SetProperty; import com.lf.annotation.SetTable ...
- 通过Java反射调用方法
这是个测试用的例子,通过反射调用对象的方法. TestRef.java import java.lang.reflect.Method; import java.lang.reflect.In ...
- java反射调用dubbo接口
需求:项目增加幂等 场景:1.三个项目:a .b.c2.a项目加幂等3.b项目dubbo调用项目a的时候超时没有获取返回结果,增加重试机制(非立即重试,3min or 5min 后重试)4.c项目是一 ...
- Java 反射 调用私有构造方法
单例类: package singleton; public class SingletonTest { // 私有构造方法 private SingletonTest(){ System.out.p ...
- java黑魔法-反射机制-02-通过Java反射调用其他类方法
package com.aaron.reflect; import java.lang.reflect.Method; import java.lang.reflect.InvocationTarge ...
- java反射调用api
cglib的fastmethod 简单示例: FastClass serviceFastClass = FastClass.create(Person.class); Person p = new P ...
- Java 反射调用方法 - 不跳过安全检查、跳过安全检查和普通方法性能比较测试
java中反射提供灵活性同时,给运行效率带来了一定影响.写个代码测试一下 package com.xzlf.reflectTest; import java.lang.reflect.Method; ...
随机推荐
- jsp电子商务 购物车实现之一 设计篇
购物车的功能实现. 查询的资料,找到三种方法: 1.用cookie实现购物车: 2.用session实现购物车: 3.用cookie和数据库(购物车信息持久化)实现购物车: ============= ...
- ionic改tab文字和icon图片的颜色
The official way would be: Change in your theme/variables.scss Active icon for tabs on android is: $ ...
- 旋转数组 [ LeetCode ]
原题地址:https://leetcode-cn.com/problems/rotate-array/description/ 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. ...
- Qt5 界面中文乱码问题
1.文件所在项目文件 xxx.pro 中添加: QMAKE_CXXFLAGS += -execution-charset:utf- 2.文件以 UTF-8 编码保存 3.添加 utf-8 BOM
- 关闭listener监听日志
有几次碰到过由于监听日志文件大小达到几G,使得在连接时非常慢,像hang住一样,windows下的监听日志达到4G限制,后续连接如果无法写监听日志,就会产生TNS-12537报错,可以通过关闭写监听日 ...
- 批量添加公钥给server的bash
Bash 脚本 #/bin/bash adduser vlmonitor mkdir -p /home/vlmonitor/.ssh chown -R vlmonitor.vlmonitor /hom ...
- js操作div的显隐
<!DOCTYPE html><html> <head> <title> new document </title> <meta ht ...
- js实现快速排序的方法
因为面试面到了这个问题,所以写一下,加深印象,有两种方法 第一种是通过两个for循环,每一次对比相邻两个数据的大小,小的排在前面,如果前面的数据比后面的大就交换这两个数的位置,这个方法就是比较次数太多 ...
- Intelij idea新窗口打开项目设置
1.idea 打开后 file->setting 2.appearance&behave->system setting->open project in new win ...
- openstack中region、az、host aggregate、cell 概念
1. region 更像是一个地理上的概念,每个region有自己独立的endpoint,regions之间完全隔离,但是多个regions之间共享同一个keystone和dashboard.(注:目 ...