對各種方法實現get方法的性能進行了一個測試。
總共有5個測試,,每個測試都是執行1億次
1. 直接通過Java的get方法
2.通過高性能的ReflectAsm庫進行測試
3.通過Java Class類自帶的反射獲得Method測試
4.使用Java自帶的Property類獲取Method測試
5.BeanUtils的getProperty測試
1 測試用Bean類
測試定義了如下一個bean類。
public class SimpleBean { private String name; public String getName() { return name; } public SimpleBean setName(String name) { this.name = name; } }
注意定義要嚴格遵守JavaBean規范,否則在使用和反射相關工具時會出現NoSuchMethodException異常,或者導致性能非常差,JavaBean規范中最重要的幾點如下:
1.類必須是public, 擁有public無參構造器,這樣能夠通過反射newInstance()動態構建對象. String className = ...; Class beanClass = Class.forName(className); Object beanInstance = beanClass.newInstance(); 2.因為反射newInstance使用的是無參構造器, 所以對象實例化和配置是分開的 3.每一個property都有一個public的getter和setter方法, 命名方式是get/set+首字母大寫的property名
經測試在SimpleBean為public時,1億次調用method.invoke方法:
javaReflectGet 100000000 times using 218 ms
而SimpleBean為默認包可見時,1一億次調用method.invoke方法:
javaReflectGet 100000000 times using 12955 ms
2.測試代碼
public class TestIterator { private long times = 100_000_000L; private SimpleBean bean; private String formatter = "%s %d times using %d ms"; @Before public void setUp() throws Exception { bean = new SimpleBean(); bean.setName("haoyifen"); } //直接通過Java的get方法 @Test public void directGet() { Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { bean.getName(); } watch.stop(); String result = String.format(formatter, "directGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //通過高性能的ReflectAsm庫進行測試,僅進行一次methodAccess獲取 @Test public void reflectAsmGet() { MethodAccess methodAccess = MethodAccess.get(SimpleBean.class); Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { methodAccess.invoke(bean, "getName"); } watch.stop(); String result = String.format(formatter, "reflectAsmGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //通過Java Class類自帶的反射獲得Method測試,僅進行一次method獲取 @Test public void javaReflectGet() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { Method getName = SimpleBean.class.getMethod("getName"); Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { getName.invoke(bean); } watch.stop(); String result = String.format(formatter, "javaReflectGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //使用Java自帶的Property屬性獲取Method測試,僅進行一次method獲取 @Test public void propertyGet() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IntrospectionException { Method method = null; BeanInfo beanInfo = Introspector.getBeanInfo(SimpleBean.class); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { if (propertyDescriptor.getName().equals("name")) { method = propertyDescriptor.getReadMethod(); break; } } Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { method.invoke(bean); } watch.stop(); String result = String.format(formatter, "propertyGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } //BeanUtils的getProperty測試 @Test public void beanUtilsGet() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { Stopwatch watch = Stopwatch.createStarted(); for (long i = 0; i < times; i++) { BeanUtils.getProperty(bean, "name"); } watch.stop(); String result = String.format(formatter, "beanUtilsGet", times, watch.elapsed(TimeUnit.MILLISECONDS)); System.out.println(result); } }
3.測試結果
在4核i5-4590@3.30GHz機器上跑以上測試,經過多次測量,基本在以下數值范圍附近,測試數據如下:
1. directGet 100000000 times using 37 ms
2. reflectAsmGet 100000000 times using 39 ms
3. javaReflectGet 100000000 times using 222 ms
4. propertyGet 100000000 times using 335 ms
5. beanUtilsGet 100000000 times using 20066 ms
4.結果分析
1.使用reflectAsm庫的性能能和直接調用get方法持平
2.Java自帶的反射性能大致為直接get的1/6和1/9.
3.BeanUtils的getProperty非常的慢,為直接get性能的1/500,為Java自帶反射性能的1/100和1/60.
為什么BeanUtils的getProperty方法性能這么慢?