接上文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的解析和驗證上。