1. 程式人生 > >過載copyProperties,使其支援Map型別

過載copyProperties,使其支援Map型別

    最近,專案組要用到一個功能,就是用BeanUtils.copyProperties複製一個Map裡的屬性值到另外一個物件。

    BeanUtils和PropertyUtils類是許多開源框架中頻繁使用的兩個工具,它們都能實現將一個類中的屬性拷貝到另一個類中,這個功能甚至是spring實現依賴注入的基礎。研究一下apache的comon包中如何實現這個兩個工具,可以發現它們都是使用java.lang.reflect和java.beans這兩個包下的幾個類來實現的。

    但是BeanUtils.copyProperties只支援兩個物件之間的複製,其原理:是利用反射讀取到第一個物件(源類)的所有屬性,然後對這些屬性集合進行for迴圈,再在for迴圈裡面判斷這些屬性是否有set方法,有則再對第二個物件(目標類)進行迴圈取出屬性一一對比,相等則呼叫目標類的set方法得到源類的get方法得到的值。

    改後主要就是兩點:第一:源類(Map型別)的Key作為屬性和目標類的屬性對比,相等則取出此Key的Value賦給目標類(當然還是用目標類此屬性的set方法)。注意:如果是頁面得到的getParameterMap()這樣的Map,其值是一個數組,一般只需要取第0項就可以了。

原始碼:

  1. /** 實現將源類屬性拷貝到目標類中
  2.        * @param source 
  3.        * @param target
  4.        */
  5. publicstaticvoid copyProperties(Object source, Object target) {
  6. try {
  7. // 獲取目標類的屬性資訊
  8.             BeanInfo targetbean = Introspector.getBeanInfo(target.getClass());
  9.             PropertyDescriptor[] propertyDescriptors = targetbean
  10.                     .getPropertyDescriptors();
  11. // 對每個目標類的屬性查詢set方法,並進行處理
  12. for (int i = 0; i < propertyDescriptors.length; i++) {
  13.                 PropertyDescriptor pro = propertyDescriptors[i];
  14.                 Method wm = pro.getWriteMethod();
  15. if (wm != null) {// 當目標類的屬性具有set方法時,查詢源類中是否有相同屬性的get方法
  16.                     BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass());
  17.                     PropertyDescriptor[] sourcepds = sourceBean.getPropertyDescriptors();
  18. for (int j = 0; j < sourcepds.length; j++) {
  19. if (sourcepds[j].getName().equals(pro.getName())) { // 匹配
  20.                             Method rm = sourcepds[j].getReadMethod();
  21. // 如果方法不可訪問(get方法是私有的或不可達),則丟擲SecurityException
  22. if (!Modifier.isPublic(rm.getDeclaringClass().getModifiers())) {
  23.                                 rm.setAccessible(true);
  24.                             }
  25. // 獲取對應屬性get所得到的值
  26.                             Object value = rm.invoke(source, new Object[0]);
  27. if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
  28.                                 wm.setAccessible(true);
  29.                             }
  30. // 呼叫目標類對應屬性的set方法對該屬性進行填充
  31.                             wm.invoke((Object) target, new Object[] { value });
  32. break;
  33.                         }
  34.                     }
  35.                 }
  36.             }
  37.         } catch (IntrospectionException e) {
  38.             e.printStackTrace();
  39.         } catch (IllegalArgumentException e) {
  40.             e.printStackTrace();
  41.         } catch (IllegalAccessException e) {
  42.             e.printStackTrace();
  43.         } catch (InvocationTargetException e) {
  44.             e.printStackTrace();
  45.         }
  46.     }

修改後支援Map的程式碼:

  1. /**
  2.      * 實現將源類屬性拷貝到目標類中
  3.      * 
  4.      * @param Map map
  5.      * @param Object obj
  6.      */
  7. publicstaticvoid copyProperties(Map map, Object obj) throws Exception {
  8. // 獲取目標類的屬性資訊
  9.         BeanInfo targetbean = Introspector.getBeanInfo(obj.getClass());
  10.         PropertyDescriptor[] propertyDescriptors = targetbean.getPropertyDescriptors();
  11. // 對每個目標類的屬性查詢set方法,並進行處理
  12. for (int i = 0; i < propertyDescriptors.length; i++) {
  13.             PropertyDescriptor pro = propertyDescriptors[i];
  14.             Method wm = pro.getWriteMethod();
  15. if (wm != null) {// 當目標類的屬性具有set方法時,查詢源類中是否有相同屬性的get方法
  16.                 Iterator ite = map.keySet().iterator();
  17. while (ite.hasNext()) {
  18.                     String key = (String) ite.next();
  19. // 判斷匹配
  20. if (key.equals(pro.getName())) {
  21. if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
  22.                             wm.setAccessible(true);
  23.                         }
  24.                         Object value = ((String[]) map.get(key))[0];
  25. // 呼叫目標類對應屬性的set方法對該屬性進行填充
  26.                         wm.invoke((Object) obj, new Object[] { value });
  27. break;
  28.                     }
  29.                 }
  30.             }
  31.         }
  32.     }

上次寫的那個方法只適用於String型別。今天擴充套件了一下,寫成了一個類,支援int/Integer、Date和自定義物件。

  1. import java.beans.BeanInfo;
  2. import java.beans.Introspector;
  3. import java.beans.PropertyDescriptor;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Modifier;
  7. import java.text.ParseException;
  8. import java.text.SimpleDateFormat;
  9. import java.util.Date;
  10. import java.util.Iterator;
  11. import java.util.Map;
  12. publicclass BeanUtils {
  13. publicstatic String DATE_FORMAT = "yyyy-MM-dd";
  14. publicstatic String[] TYPE_SIMPLE = {"java.lang.Integer","int","java.util.Date"};
  15. publicstatic String TYPE_INTEGER = "java.lang.Integer,int";
  16. publicstatic String TYPE_DATE = "java.util.Date";
  17. /**
  18.      * 得到空格之後的字元
  19.      * 
  20.      * @param String type
  21.      * @param String str
  22.      * @return Date
  23.      * @throws ParseException
  24.      */
  25. publicstatic String splitSpace(String str) throws ParseException{
  26. if(str.contains(" ")){
  27. return str.split(" ")[1];
  28.         } else {
  29. return str;
  30.         }
  31.     }
  32. /**
  33.      * 判斷是否是簡單資料型別
  34.      * 
  35.      * @param String type
  36.      */
  37. publicstaticboolean isSimpleType(String type) {
  38. for (int i = 0; i < TYPE_SIMPLE.length; i++) {
  39. if (type.equals(TYPE_SIMPLE[i])) {
  40. returntrue;
  41.             }
  42.         }
  43. returnfalse;
  44.     }
  45. /**
  46.      * 把String型別轉換為Integer
  47.      * 
  48.      * @param String str
  49.      * @return Integer
  50.      */
  51. publicstatic Integer parseInteger(String str){
  52. if(str == null || str.equals("")){
  53. return0;
  54.         } else {
  55. return Integer.parseInt(str);
  56.         }
  57.     }
  58. /**
  59.      * 把String型別轉換為Date
  60.      * 
  61.      * @param String str
  62.      * @return Date
  63.      * @throws ParseException
  64.      */
  65. publicstatic Date parseDate(String str) throws ParseException{
  66. if(str == null || str.equals("")){
  67. returnnull;
  68.         } else {
  69.             SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
  70.             Date date = sdf.parse(str);
  71. return date;
  72.         }
  73.     }
  74. /**
  75.      * 轉換物件(使用者定義的物件)。設定物件的Id。
  76.      * 
  77.      * @param Class clazz
  78.      * @param  String str
  79.      * @return Object
  80.      * @throws IllegalAccessException 
  81.      * @throws InstantiationException 
  82.      * @throws NoSuchMethodException 
  83.      * @throws SecurityException 
  84.      * @throws InvocationTargetException 
  85.      * @throws IllegalArgumentException 
  86.      * @throws ParseException
  87.      */
  88. publicstatic Object parseObject(Class clazz, String str) throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
  89.         Object obj;
  90. if(str == null || str.equals("")){
  91.             obj = null;
  92.         } else {
  93.             obj = clazz.newInstance();
  94.             Method m = clazz.getMethod("setId",str.getClass());
  95.             m.invoke(obj,str);
  96.         }
  97. return obj;
  98.     }
  99. /**
  100.      * 根據型別進行轉換
  101.      * 
  102.      * @param Class clazz
  103.      * @param String str
  104.      * @return Object
  105.      * @throws ParseException
  106.      * @throws IllegalAccessException 
  107.      * @throws InstantiationException 
  108.      * @throws InvocationTargetException 
  109.      * @throws NoSuchMethodException 
  110.      * @throws IllegalArgumentException 
  111.      * @throws SecurityException 
  112.      */
  113. publicstatic Object parseByType(Class clazz, String str) throws ParseException, InstantiationException, IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException{
  114.         Object r = "";
  115.         String clazzName = splitSpace(clazz.getName());
  116. if (isSimpleType(clazzName)){
  117. if (TYPE_INTEGER.contains(clazzName)) {
  118.                 r = parseInteger(str);
  119.             } elseif (TYPE_DATE.contains(clazzName)) {
  120.                 r = parseDate(str);
  121.             }
  122.         } else {
  123.             r = parseObject(clazz, str);
  124.         }
  125. return r;
  126.     }
  127. /** 實現將源類(Map型別)屬性拷貝到目標類中
  128.        * @param Map map 
  129.        * @param Object obj
  130.        */
  131. publicstaticvoid copyProperties(Map map, Object obj) throws Exception {
  132. // 獲取目標類的屬性資訊
  133.         BeanInfo targetbean = Introspector.getBeanInfo(obj.getClass());
  134.         PropertyDescriptor[] propertyDescriptors = targetbean.getPropertyDescriptors();
  135. // 對每個目標類的屬性查詢set方法,並進行處理
  136. for (int i = 0; i < propertyDescriptors.length; i++) {
  137.             PropertyDescriptor pro = propertyDescriptors[i];
  138.             Method wm = pro.getWriteMethod();
  139. if (wm != null) {
  140.                 Iterator ite = map.keySet().iterator();
  141. while (ite.hasNext()) {
  142.                     String key = (String) ite.next();
  143. // 判斷匹配
  144. if (key.toLowerCase().equals(pro.getName().toLowerCase())) {
  145. if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
  146.                             wm.setAccessible(true);
  147.                         }
  148.                         Object value = ((String[]) map.get(key))[0];
  149.                         String pt = splitSpace(pro.getPropertyType().getName());
  150. //判斷型別是否匹配,不匹配則作強制轉換
  151. if (!(pt.equals(value.getClass().getName()))) {
  152.                             value = parseByType(pro.getPropertyType(),value.toString());
  153.                         }
  154. // 呼叫目標類對應屬性的set方法對該屬性進行填充
  155.                         wm.invoke((Object) obj, new Object[] {value});
  156. break;
  157.                     }
  158.                 }
  159.             }
  160.         }
  161.     }
  162. }