copyPropertie和PropertyUtils.copyProperties() 代替get、set方法及ReflectASM提高效率

轉自:http://blog.csdn.net/liaodehong/article/details/50379351

背景:

在電商項目中所需要的業務非常多,所以我們的業務數據也會有很多種,這個時候就會有什麼VO,DTO,PO等等這些,把業務和我們的基礎數據進行分離轉換。但是一直都沒有什麼好一點的轉換類。後來用了一下BeanUtils.copyPropertie,和PropertyUtils.copyProperties()的方法,發現其效率非常低。這裏也簡單總結了一下他們的用法及原理以及自己實現的轉換類;

用法:

[java] view plain copy
 print?
  1. BeanUtils.copyProperties("轉換後的類""要轉換的類");  
[java] view plain copy
 print?
  1. PropertyUtils.copyProperties("轉換後的類""要轉換的類");  
用法其實很簡單,第一個參數是轉換後的類,第二個參數是待轉換的類;我們可以理解成爲後轉前;

原理:

其原理是通過JDK自帶的反射機制動態的去get,set,從而去轉換我們的類。但是要注意一點他們所支持的數據類型,還有一個就是假如一個類裏面又寫了一個類,例如這種:

[java] view plain copy
 print?
  1. public class Name{  
  2.   
  3. }  
  4. class Name1{  
  5.   
  6. }  
一般叫做內部類,像這種類進行轉換的時候,是不會成功的。因爲在裏面進行讀寫校驗的時候不會通過;

[java] view plain copy
 print?
  1. PropertyDescriptor[] origDescriptors =  
  2.                 getPropertyDescriptors(orig);  
  3.             for (int i = 0; i < origDescriptors.length; i++) {  
  4.                 String name = origDescriptors[i].getName();  
  5.                 if (isReadable(orig, name) && isWriteable(dest, name)) {  
  6.                     try {  
  7.                         Object value = getSimpleProperty(orig, name);  
  8.                         if (dest instanceof DynaBean) {  
  9.                             ((DynaBean) dest).set(name, value);  
  10.                         } else {  
  11.                                 setSimpleProperty(dest, name, value);  
  12.                         }  
  13.                     } catch (NoSuchMethodException e) {  
  14.                         if (log.isDebugEnabled()) {  
  15.                             log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", e);  
  16.                         }  
  17.                     }  
  18.                 }  
  19.             }  
上圖是JDK的源代碼,我們執行isRead和isWrite的時候並不會通過;

注意差異,

主要的區別在於BeanUtils
提供類型轉換功能,即發現兩個JavaBean的同名屬性爲不同類型時,在支持的數據類型範圍內進行轉換,而前者不支持這個功能,但是速度會更快一些。BeanUtils支持的轉換類型如下:

[java] view plain copy
 print?
  1. * java.lang.BigDecimal  
  2. * java.lang.BigInteger  
  3. boolean and java.lang.Boolean  
  4. byte and java.lang.Byte  
  5. char and java.lang.Character  
  6. * java.lang.Class  
  7. double and java.lang.Double  
  8. float and java.lang.Float  
  9. int and java.lang.Integer  
  10. long and java.lang.Long  
  11. short and java.lang.Short  
  12. * java.lang.String  
  13. * java.sql.Date  
  14. * java.sql.Time  
  15. * java.sql.Timestamp   

閱讀其源碼,發現其內部是使用了裝飾者模式,我發現Java得工具類很喜歡使用這種模式,而且也十分好用;

他們都使用到了BeanutilsBean和PropertyUtilsBean只不過BeanUtils多了一個轉換的功能而已,但是性能上要比

PropertyUtils慢一些,其實兩個都很慢,最好不要使用;

重寫反射轉換:

[java] view plain copy
 print?
  1. /** 
  2.      * @param obj 轉換的對象值 
  3.      * @param clz 類對象 
  4.      * @return 轉換後的對象 
  5.      */  
  6.     public static<T> T transferObject(Object obj,Class clz){  
  7.         T result = null;  
  8.         if(obj!=null&&!obj.equals("")){  
  9.         Method[] methods =  obj.getClass().getMethods();  
  10.         try {  
  11.             result = (T)clz.newInstance();  
  12.         } catch (Exception e1) {  
  13.             return null;  
  14.         }  
  15.         Method m;  
  16.         for(int i=0;i<methods.length;i++){  
  17.             m = methods[i];  
  18.             try {  
  19.                 if(m.getName().startsWith("set")){  
  20.                     String fieldName =  m.getName().replaceFirst("set""");  
  21.                     Method method = result.getClass().getMethod(m.getName(), m.getParameterTypes());  
  22.                     Method getMethod = obj.getClass().getMethod("get"+fieldName, new Class[]{});  
  23.                     method.invoke(result, getMethod.invoke(obj, new Object[]{}));  
  24.                 }  
  25.             } catch (Exception e) {  
  26.                 continue;  
  27.             }  
  28.         }  
  29.         }  
  30.         return result;  
  31.     }  
上面這個方法也是用了Java反射去寫的,但是少了很多校驗以及轉換。所以在100萬條數據的時候,效率是3739毫秒,而使用BeanUtils是5000毫秒左右。兩個效率都不高;

ReflectASM,高性能的反射:

什麼是ReflectASM    ReflectASM是一個很小的java類庫,主要是通過asm生產類來實現java反射,執行速度非常快,看了網上很多和反射的對比,覺得ReflectASM比較神奇,很想知道其原理,下面介紹下如何使用及原理;

[java] view plain copy
 print?
  1. public static void main(String[] args) {    
  2.         User user = new User();    
  3.         //使用reflectasm生產User訪問類    
  4.         MethodAccess access = MethodAccess.get(User.class);    
  5.         //invoke setName方法name值    
  6.         access.invoke(user, "setName""張三");    
  7.         //invoke getName方法 獲得值    
  8.         String name = (String)access.invoke(user, "getName"null);    
  9.         System.out.println(name);    
  10.     }    
原理 
   上面代碼的確實現反射的功能,代碼主要的核心是 MethodAccess.get(User.class); 
看了下源碼,這段代碼主要是通過asm生產一個User的處理類 UserMethodAccess(這個類主要是實現了invoke方法)的ByteCode,然後獲得該對象,通過上面的invoke操作user類。 

ASM反射轉換:

[java] view plain copy
 print?
  1. private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();  
  2.   
  3.     private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();  
  4.   
  5.     private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();  
  6.   
  7.     public static void copyProperties(Object desc, Object orgi) {  
  8.         MethodAccess descMethodAccess = methodMap.get(desc.getClass());  
  9.         if (descMethodAccess == null) {  
  10.             descMethodAccess = cache(desc);  
  11.         }  
  12.         MethodAccess orgiMethodAccess = methodMap.get(orgi.getClass());  
  13.         if (orgiMethodAccess == null) {  
  14.             orgiMethodAccess = cache(orgi);  
  15.         }  
  16.   
  17.         List<String> fieldList = fieldMap.get(orgi.getClass());  
  18.         for (String field : fieldList) {  
  19.             String getKey = orgi.getClass().getName() + "." + "get" + field;  
  20.             String setkey = desc.getClass().getName() + "." + "set" + field;  
  21.             Integer setIndex = methodIndexMap.get(setkey);  
  22.             if (setIndex != null) {  
  23.                 int getIndex = methodIndexMap.get(getKey);  
  24.                 // 參數一需要反射的對象  
  25.                 // 參數二class.getDeclaredMethods 對應方法的index  
  26.                 // 參數對三象集合  
  27.                 descMethodAccess.invoke(desc, setIndex.intValue(),  
  28.                         orgiMethodAccess.invoke(orgi, getIndex));  
  29.             }  
  30.         }  
  31.     }  
  32.   
  33.     // 單例模式  
  34.     private static MethodAccess cache(Object orgi) {  
  35.         synchronized (orgi.getClass()) {  
  36.             MethodAccess methodAccess = MethodAccess.get(orgi.getClass());  
  37.             Field[] fields = orgi.getClass().getDeclaredFields();  
  38.             List<String> fieldList = new ArrayList<String>(fields.length);  
  39.             for (Field field : fields) {  
  40.                 if (Modifier.isPrivate(field.getModifiers())  
  41.                         && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是靜態的  
  42.                     // 非公共私有變量  
  43.                     String fieldName = StringUtils.capitalize(field.getName()); // 獲取屬性名稱  
  44.                     int getIndex = methodAccess.getIndex("get" + fieldName); // 獲取get方法的下標  
  45.                     int setIndex = methodAccess.getIndex("set" + fieldName); // 獲取set方法的下標  
  46.                     methodIndexMap.put(orgi.getClass().getName() + "." + "get"  
  47.                             + fieldName, getIndex); // 將類名get方法名,方法下標註冊到map中  
  48.                     methodIndexMap.put(orgi.getClass().getName() + "." + "set"  
  49.                             + fieldName, setIndex); // 將類名set方法名,方法下標註冊到map中  
  50.                     fieldList.add(fieldName); // 將屬性名稱放入集合裏  
  51.                 }  
  52.             }  
  53.             fieldMap.put(orgi.getClass(), fieldList); // 將類名,屬性名稱註冊到map中  
  54.             methodMap.put(orgi.getClass(), methodAccess);  
  55.             return methodAccess;  
  56.         }  
  57.     }  
執行1000000條效率80幾毫秒,效率已經沒問題了;



發佈了30 篇原創文章 · 獲贊 13 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章