過載copyProperties,使其支援Map型別
阿新 • • 發佈:2019-01-22
最近,專案組要用到一個功能,就是用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項就可以了。
原始碼:
- /** 實現將源類屬性拷貝到目標類中
- * @param source
- * @param target
- */
- publicstaticvoid copyProperties(Object source, Object target) {
- try {
- // 獲取目標類的屬性資訊
- BeanInfo targetbean = Introspector.getBeanInfo(target.getClass());
- PropertyDescriptor[] propertyDescriptors = targetbean
- .getPropertyDescriptors();
- // 對每個目標類的屬性查詢set方法,並進行處理
- for (int i = 0; i < propertyDescriptors.length; i++) {
- PropertyDescriptor pro = propertyDescriptors[i];
- Method wm = pro.getWriteMethod();
- if (wm != null) {// 當目標類的屬性具有set方法時,查詢源類中是否有相同屬性的get方法
- BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass());
- PropertyDescriptor[] sourcepds = sourceBean.getPropertyDescriptors();
- for (int j = 0; j < sourcepds.length; j++) {
- if (sourcepds[j].getName().equals(pro.getName())) { // 匹配
- Method rm = sourcepds[j].getReadMethod();
- // 如果方法不可訪問(get方法是私有的或不可達),則丟擲SecurityException
- if (!Modifier.isPublic(rm.getDeclaringClass().getModifiers())) {
- rm.setAccessible(true);
- }
- // 獲取對應屬性get所得到的值
- Object value = rm.invoke(source, new Object[0]);
- if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
- wm.setAccessible(true);
- }
- // 呼叫目標類對應屬性的set方法對該屬性進行填充
- wm.invoke((Object) target, new Object[] { value });
- break;
- }
- }
- }
- }
- } catch (IntrospectionException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
修改後支援Map的程式碼:
- /**
- * 實現將源類屬性拷貝到目標類中
- *
- * @param Map map
- * @param Object obj
- */
- publicstaticvoid copyProperties(Map map, Object obj) throws Exception {
- // 獲取目標類的屬性資訊
- BeanInfo targetbean = Introspector.getBeanInfo(obj.getClass());
- PropertyDescriptor[] propertyDescriptors = targetbean.getPropertyDescriptors();
- // 對每個目標類的屬性查詢set方法,並進行處理
- for (int i = 0; i < propertyDescriptors.length; i++) {
- PropertyDescriptor pro = propertyDescriptors[i];
- Method wm = pro.getWriteMethod();
- if (wm != null) {// 當目標類的屬性具有set方法時,查詢源類中是否有相同屬性的get方法
- Iterator ite = map.keySet().iterator();
- while (ite.hasNext()) {
- String key = (String) ite.next();
- // 判斷匹配
- if (key.equals(pro.getName())) {
- if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
- wm.setAccessible(true);
- }
- Object value = ((String[]) map.get(key))[0];
- // 呼叫目標類對應屬性的set方法對該屬性進行填充
- wm.invoke((Object) obj, new Object[] { value });
- break;
- }
- }
- }
- }
- }
上次寫的那個方法只適用於String型別。今天擴充套件了一下,寫成了一個類,支援int/Integer、Date和自定義物件。
- import java.beans.BeanInfo;
- import java.beans.Introspector;
- import java.beans.PropertyDescriptor;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Iterator;
- import java.util.Map;
- publicclass BeanUtils {
- publicstatic String DATE_FORMAT = "yyyy-MM-dd";
- publicstatic String[] TYPE_SIMPLE = {"java.lang.Integer","int","java.util.Date"};
- publicstatic String TYPE_INTEGER = "java.lang.Integer,int";
- publicstatic String TYPE_DATE = "java.util.Date";
- /**
- * 得到空格之後的字元
- *
- * @param String type
- * @param String str
- * @return Date
- * @throws ParseException
- */
- publicstatic String splitSpace(String str) throws ParseException{
- if(str.contains(" ")){
- return str.split(" ")[1];
- } else {
- return str;
- }
- }
- /**
- * 判斷是否是簡單資料型別
- *
- * @param String type
- */
- publicstaticboolean isSimpleType(String type) {
- for (int i = 0; i < TYPE_SIMPLE.length; i++) {
- if (type.equals(TYPE_SIMPLE[i])) {
- returntrue;
- }
- }
- returnfalse;
- }
- /**
- * 把String型別轉換為Integer
- *
- * @param String str
- * @return Integer
- */
- publicstatic Integer parseInteger(String str){
- if(str == null || str.equals("")){
- return0;
- } else {
- return Integer.parseInt(str);
- }
- }
- /**
- * 把String型別轉換為Date
- *
- * @param String str
- * @return Date
- * @throws ParseException
- */
- publicstatic Date parseDate(String str) throws ParseException{
- if(str == null || str.equals("")){
- returnnull;
- } else {
- SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
- Date date = sdf.parse(str);
- return date;
- }
- }
- /**
- * 轉換物件(使用者定義的物件)。設定物件的Id。
- *
- * @param Class clazz
- * @param String str
- * @return Object
- * @throws IllegalAccessException
- * @throws InstantiationException
- * @throws NoSuchMethodException
- * @throws SecurityException
- * @throws InvocationTargetException
- * @throws IllegalArgumentException
- * @throws ParseException
- */
- publicstatic Object parseObject(Class clazz, String str) throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
- Object obj;
- if(str == null || str.equals("")){
- obj = null;
- } else {
- obj = clazz.newInstance();
- Method m = clazz.getMethod("setId",str.getClass());
- m.invoke(obj,str);
- }
- return obj;
- }
- /**
- * 根據型別進行轉換
- *
- * @param Class clazz
- * @param String str
- * @return Object
- * @throws ParseException
- * @throws IllegalAccessException
- * @throws InstantiationException
- * @throws InvocationTargetException
- * @throws NoSuchMethodException
- * @throws IllegalArgumentException
- * @throws SecurityException
- */
- publicstatic Object parseByType(Class clazz, String str) throws ParseException, InstantiationException, IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException{
- Object r = "";
- String clazzName = splitSpace(clazz.getName());
- if (isSimpleType(clazzName)){
- if (TYPE_INTEGER.contains(clazzName)) {
- r = parseInteger(str);
- } elseif (TYPE_DATE.contains(clazzName)) {
- r = parseDate(str);
- }
- } else {
- r = parseObject(clazz, str);
- }
- return r;
- }
- /** 實現將源類(Map型別)屬性拷貝到目標類中
- * @param Map map
- * @param Object obj
- */
- publicstaticvoid copyProperties(Map map, Object obj) throws Exception {
- // 獲取目標類的屬性資訊
- BeanInfo targetbean = Introspector.getBeanInfo(obj.getClass());
- PropertyDescriptor[] propertyDescriptors = targetbean.getPropertyDescriptors();
- // 對每個目標類的屬性查詢set方法,並進行處理
- for (int i = 0; i < propertyDescriptors.length; i++) {
- PropertyDescriptor pro = propertyDescriptors[i];
- Method wm = pro.getWriteMethod();
- if (wm != null) {
- Iterator ite = map.keySet().iterator();
- while (ite.hasNext()) {
- String key = (String) ite.next();
- // 判斷匹配
- if (key.toLowerCase().equals(pro.getName().toLowerCase())) {
- if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
- wm.setAccessible(true);
- }
- Object value = ((String[]) map.get(key))[0];
- String pt = splitSpace(pro.getPropertyType().getName());
- //判斷型別是否匹配,不匹配則作強制轉換
- if (!(pt.equals(value.getClass().getName()))) {
- value = parseByType(pro.getPropertyType(),value.toString());
- }
- // 呼叫目標類對應屬性的set方法對該屬性進行填充
- wm.invoke((Object) obj, new Object[] {value});
- break;
- }
- }
- }
- }
- }
- }