比FastMethod更快的Java反射调用API,以及比Cglib更快的BeanMap实现
动态调用方法,大多数情况下,只能使用反射机制实现。反射性能上远远逊于直接调用(虽然JDK7中的MethodHandle也可以稍微提高动态调用性能,但JDK7在企业应用中暂时还无法普及)。
反射调用之所以性能低下,主要是因为通用接口对参数进行数组封装和解封导致,而大多数情况下,反射调用时的参数数量和类型,我们是知道的。那么,能不能动态调用方法时不去对参数进行封装和解封,从而达到提高动态调用的效果呢?
基于此理念,实现了Invokers类。使用该类时,先行定义好调用接口,调用接口包含了需要反射调用的方法的参数列表以及返回值的接口方法(方法名可以和反射方法名不一致,且参数列表的第一个参数是反射调用的对象,其它参数才是反射调用时需要的参数)。接口定义好之后,可以通过Invokers生成该接口的实例,然后通过该实例来实现动态调用。例子如下:
上面的单元测试中,分别通过不同方式调用一亿次Date类型的getTime和setTime方法,输出如下:
time=1362806708572
time1=33333333
time2=33333333
直接调用耗时:18ms
Invokers调用耗时:120ms
JDK反射调用耗时:5352ms
FastMethod调用耗时:5057ms
可见Invokes的性能和直接调用已经接近了(实际上,如果接口处声明为准确参数类型而不是通用类型的话,会和直接调用性能一样),和传统反射相比相差两个数量级。
注:测试机器是Macbook Pro 374,虚拟机是JDK 6。
另一个例子,实现传统的通用反射接口(性能也是最好的)。测试代码如下:
输出结果如下(和上面的单元测试一样的环境):
1362857121037
333333
Invoker调用耗时:131ms
JDK反射调用耗时:5106ms
CGLIB反射调用耗时:4610ms
Invokers的代码如下(需javassist支持,asm生成字节码太麻烦):
以Invokers为基础,自己实现了一个BeanMap,代码如下:
。但常规使用下,经常是对新对象创建Map并且只需要遍历执行一次get/put,所以创建时间慢的话还是严重影响性能的,使用此BeanMap在创建上速度是cglib的10倍以上。很多开源库都依赖于BeanMap,如JSON解析等等,如能使用此API,相信相关库的性能也会跟着大幅提升。