接上文Java各种反射性能对比
BeanUtils.getProperty的原理其实以下方法类似,但稍有不同
- //代码片段4.1
PropertyDescriptor descriptor=null;
BeanInfo beanInfo =Introspector.getBeanInfo(SimpleBean.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor propertyDescriptor : propertyDescriptors){
if(propertyDescriptor.getName().equals("name")){
descriptor=propertyDescriptor;
break;
}
}
for(long i =0; i < times; i++){
descriptor.getReadMethod().invoke(bean);
}
先获取BeanInfo,然后获取所有PropertyDescriptors, 通过与想要获取的属性名对比,比如“name”,来获取对应的propertyDescriptor,最后循环getReadMethod和invoke。
上面的测试消耗了大概2800ms( 因为只缓存了对应的PrepertyDescriptor,而不是对应的method ),BeanUtils.getProperty性能仍只有其1/7。
最开始是怀疑BeanUtils.getProperty没有对获得的PropertyDescriptor进行缓存,每次都重新查找对一个你的PropertyDescriptor,通过阅读源码,发现其通过一个与当前classloader绑定的ContextClassLoaderLocal实例来缓存匹配到的property,属性valueByClassLoader就是用来保存的。
private Map valueByClassLoader = new WeakHashMap();
这样性能应该和上面的一样才对。
通过JProfiler分析调用过程,
获取PropertyDescriptor[]的调用消耗了2.9%的时间
publicPropertyDescriptor[] getPropertyDescriptors(Object bean){
if(bean ==null){
thrownewIllegalArgumentException("No bean specified");
}
return(getPropertyDescriptors(bean.getClass()));
}
获取property的readMethod消耗了6.4%的时间
Method getReadMethod(Class clazz,PropertyDescriptor descriptor){
return(MethodUtils.getAccessibleMethod(clazz, descriptor.getReadMethod()));
}
而真正的method调用只消耗了1.6%的时间
privateObject invokeMethod(
Method method,
Object bean,
Object[] values)
throws
IllegalAccessException,
InvocationTargetException{
if(bean ==null){
thrownewIllegalArgumentException("No bean specified "+
"- this should have been checked before reaching this method");
}
try{
return method.invoke(bean, values);
}catch(NullPointerException cause){
....省略
}
这些和反射有关的调用其实都没有花太多时间,3000ms×(1.6%+6.4%)=2400ms,和代码片段4.1中的2800ms基本相同.
请看以下的方法调用时间:
这些方法占了整个调用过程的54%的时间,这些是为了使BeanUtils.getProperty不仅仅能够获取bean的一级属性,还能够判断我们所传入的属性是否是嵌套属性,是否是从数组取值或者是map中取值,需要对传入的属性名不断的解析,调用String.length()和String.charAt()两个方法进行循环判断。
感觉大部分情况下,我们都只是解析一级属性,BeanUtils中提供了一个方法,getSimpleProperty,测试的时间并没有比getProperty快多少,1亿次调用时间为15299ms,主要时间和上面一样花在了传入的property name的解析和验证上。