Map與對象之間屬性複製淺析

在使用Java開發中,很多時候會遇到需要將Map中的值複製到對象中。如果通過手動方式將map中的值取出然後在set到對象中,那對於屬性比較多的情形來說,這明顯不是一個好辦法。當然有比較方便的拌辦法,就是使用Apache Commons BeanUtils中的BeanUtils.copyProperties(Object, Object)方法。但是筆者最近在使用這個方法時,遇到屬性無法複製到對象中的情況,於是對其中原因進行了分析,總結如下。

1. 示例代碼

public class TestPropertiesCopy {	
	public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
		//1.將Map中的值複製到bean中
		Map<String, Object> inputMap = new HashMap<>();
		inputMap.put("iAppNo", "123456");
		inputMap.put("iAppName", "myname");
		
		InputBody inputBody = new InputBody();
		BeanUtils.copyProperties(inputBody, inputMap);
		System.out.println(inputBody);		
	}
}

/**
 * 輸入報文實體 
 */

@Data
@ToString
class InputBody {	
	String iAppNo;
	String iAppName;	
}

當運行上述代碼時,得到的結果如下,也就是map中的屬性並沒有複製到對象的同名屬性中。

InputBody(iAppNo=null, iAppName=null)

2. 原因淺析

很明顯,這個問題肯定出在copyProperties()方法中,那這個方法具體做了些什麼呢?
當對copyProperties()方法進行跟蹤時,最終在getTargetPropertyInfo()方法中,發現瞭如下一段關鍵性代碼:

if (name.startsWith(GET_PREFIX)) {
	// Simple getter
    pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);
} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
    // Boolean getter
    pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
}

從這段代碼中可以看出,在將map中屬性的值複製到對象時,會根據對象中的getter方法(類型爲boolean時即爲is()方法)來獲取對象的屬性名,具體的實現在實例化PropertyDescriptor對象時的setName()方法的decapitalize()方法中,具體如下:

public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
    }
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

從代碼中可以看到,首先對getter方法名從第四位截取(is方法名從第三位截取),然後進行如下判斷:

  • 當首字母和第二個字母都是大寫字母時,直接返回截取後字符串作爲屬性名
  • 其他情況,則將首字母轉爲小寫再返回作爲屬性名

到這裏,也就是能解釋第1節中的代碼爲什麼不能複製成功了。lombok包在爲屬性生成getter方法時,會將屬性名的第一個字符轉成大寫,比如iAppNo屬性對應的getter方法名爲getIAppNo。當BeanUtils.copyProperties()方法對其賦值而獲取屬性名的方法,會因爲第一個和第二個字母均爲大寫爲不進行轉換,所以獲取到的屬性名爲IAppNo,這與我們預期的不一致,導致複製失敗。

3. 解決方法

找到原因後 ,修改起來相對容易,那就是命名時儘量規範,不要以單個字母開頭然後接一個單詞,如iAppNo,可以改成inAppNo,這樣就可以避免上面的不一致的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章