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