1. 程式人生 > >利用反射機制完成java物件與物件之間的轉換

利用反射機制完成java物件與物件之間的轉換

在微服務架構的專案中,我們經常會遇到外觀模式設計,把核心的資料結構隱藏起來,暴露對外的request和response資料結構。如果巢狀的層級多了,層層的資料轉換將會增加大量程式碼,讓整個專案看起來臃腫不堪。例如:

public void saveVisitStatInfo(VisitStatRequest visitStatRequest) {
    VisitStatPO visitStatPO = new VisitStatPO();
    if(visitStatRequest != null) {
        visitStatPO.setMemberId(visitStatRequest.getMemberId());
        visitStatPO.setIpAddress(visitStatRequest.getIpAddress());
        visitStatPO.setUtmCampaign(visitStatRequest.getUtmCampaign());
        visitStatPO.setUtmContent(visitStatRequest.getUtmContent());
        visitStatPO.setUtmMedium(visitStatRequest.getUtmMedium());
        visitStatPO.setUtmSource(visitStatRequest.getUtmSource());
        visitStatPO.setUtmTerm(visitStatRequest.getUtmTerm());
        visitStatService.saveVisit(visitStatPO);
    } else {
        logger.error("Visit stat input is null");
    }
}

因此我們可以找一個物件與物件之間的轉換工具,完成統一轉換。
關於java物件與物件之間的轉換,可行的方式有以下幾種
1、org.apache.commons.beanutils.PropertyUtils.copyProperties()方法
2、org.springframework.beans.BeanUtils.copyProperties(source, target)方法
3、利用json做中轉轉換
4、利用反射機制寫一個物件轉換工具

第1和第2種都不推薦使用,因為第1種的效能較差,不支援Date等型別。第2種不支援Integer,Long,Boolean等為null的傳遞,而且對於同名不同型別的資料轉換會直接報錯(網上有說只是不賦值,博主用JDK1.7親測會報錯如下)
org.springframework.beans.FatalBeanException: Could not copy properties from source to target; nested exception is java.lang.IllegalArgumentException: argument type mismatch
at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:620)
at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:530)
at test.main.main.test(main.java:119)

第3種推薦在沒什麼特殊需求的情況下使用,好處是深克隆,不擔心地址汙染,而且程式碼也簡單。
第4種則推薦當轉換過程中對賦值有特定要求的情況下使用。好處是支援所有型別和資料,而且可以自由定義轉換過程中的資料處理方式。程式碼如下:

public static Object objToObj(Object source, Class<?> targetClazz) throws InstantiationException, IllegalAccessException {
	Object target = targetClazz.newInstance();
	Class sourceClazz = source.getClass();
	
	Field[] sourceFields = sourceClazz.getDeclaredFields();
	Field[] targetFields = target.getClass().getDeclaredFields();
	
	for (Field sourceField : sourceFields) {
		sourceField.setAccessible(true);
		for (Field targetField : targetFields) {
			if(targetField.getName().equals(sourceField.getName()) && targetField.getType() == sourceField.getType()) {
				int mod = targetField.getModifiers();
				if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
					continue;
				}
				targetField.setAccessible(true);
				targetField.set(target, sourceField.get(source));
				break;
			}
		}
	}	
	return target;
}

該段程式碼主要實現的是隻有成員變數名稱和型別都一致才賦值的淺克隆物件轉換。有興趣的同學可以通過修改判定條件實現不同的資料轉換功能,也可以新增遞迴演算法實現深克隆轉換。

最後需要說明的是,無論選擇哪種物件轉換功能,都一定比直接通過set方法賦值效率要低,如何選擇需要同學們自己把控。