個人博客請訪問 http://www.x0100.top
序言
近期在工作中管理代碼時發現,在項目中從Dao層到Service層數據傳遞中通過大量的get(),set()方法去一個一個的去拿值去賦值,導致代碼篇幅過長,對此甚是討厭,並且嚴重消耗開發時間。
起初找過些關於這塊的資料,現在大部分都是Entity類和Dto類的屬性名相同的前提下,利用反射實現,太侷限了,如果要改成同名,按目前項目的程度去整改工作量太大,不現實。
後面看了Spring註解的實現,然後結合找到反射實現資料,突想奇發嘗試着用自定義註解+反射方式的去實現,事實證明這方法是可行的。故分享至此,希望能幫到大家。
整體實現三步驟:
-
自定義註解
-
工具類方法實現反射
-
使用(測試)
1.自定義註解
import java.lang.annotation.*;
@Target({ElementType.FIELD,ElementType.TYPE}) //Target 註解的使用域,FIELD表示使用在屬性上面,TYPE表示使用在類上面
@Retention(RetentionPolicy.RUNTIME) //Retention 設置註解的生命週期 ,這裏定義爲RetentionPolicy.RUNTIME 非常關鍵
@Documented
public @interface RelMapper {
//自定義屬性
String value() default "";
String type() default ""; // value : status(標記屬性值爲Y/N的屬性) / date(標記屬性類型爲時間)
}
自定義屬性,大家可以根據自己項目中的需求增加不同的屬性。
2.工具類方法實現
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import com.ctccbs.common.annotation.RelMapper;
public class RelationMapperUtils {
/**
* Entity and Dto Mapper
* @param entry
* @param dto
* @param enToDto
* ture : Entity To Dto (defult)
* false : Dto To Entry
* Rule:
* 實現相互轉換前提: Dto field name(dto和entry的field name相同並且 類上有@RelMapper) 或 field的@RelMapper(value="Entity field name") 滿足其一即可轉換
* @return
* @throws Exception
*/
public static Object entryAndDtoMapper(Object entity, Object dto) throws Exception{
return EnAndDtoMapper(entity, dto,true);
}
public static Object entryAndDtoMapper(Object entity, Object dto,boolean enToDto) throws Exception{
return EnAndDtoMapper(entity, dto,false);
}
//last version
public static Object EnAndDtoMapper(Object entry, Object dto,boolean enToDto) throws Exception{
if(enToDto == true ? entry == null : dto == null){ return null;}
Class<? extends Object> entryclazz = entry.getClass(); //獲取entity類
Class<? extends Object> dtoclazz = dto.getClass(); //獲取dto類
boolean dtoExistAnno = dtoclazz.isAnnotationPresent(RelMapper.class); //判斷類上面是否有自定義註解
Field [] dtofds = dtoclazz.getDeclaredFields(); //dto fields
Field [] entryfds = entryclazz.getDeclaredFields(); //entity fields
Method entrys[] = entryclazz.getDeclaredMethods(); //entity methods
Method dtos[] = dtoclazz.getDeclaredMethods(); //dto methods
String mName,fieldName,dtoFieldType=null,entFieldType=null,dtoMapName = null,dtoFieldName =null;Object value = null;
for(Method m : (enToDto ? dtos : entrys)) { //當 enToDto=true 此時是Entity轉爲Dto,遍歷dto的屬性
if((mName=m.getName()).startsWith("set")) { //只進set方法
fieldName = mName.toLowerCase().charAt(3) + mName.substring(4,mName.length()); //通過set方法獲得dto的屬性名
tohere:
for(Field fd: dtofds) {
fd.setAccessible(true); //setAccessible是啓用和禁用訪問安全檢查的開關
if(fd.isAnnotationPresent(RelMapper.class)||dtoExistAnno){ //判斷field上註解或類上面註解是否存在
//獲取與Entity屬性相匹配的映射值(兩種情況:1.該field上註解的value值(Entity的field name 和Dto 的field name 不同) 2.該field本身(本身則是Entity的field name 和Dto 的field name 相同))
dtoMapName = fd.isAnnotationPresent(RelMapper.class) ? (fd.getAnnotation(RelMapper.class).value().toString().equals("")?fd.getName().toString():fd.getAnnotation(RelMapper.class).value().toString()):fd.getName().toString();
if(((enToDto ? fd.getName() : dtoMapName)).toString().equals(fieldName)) {
dtoFieldType = fd.getGenericType().toString().substring(fd.getGenericType().toString().lastIndexOf(".") + 1); // 獲取dto屬性的類型(如 private String field 結果 = String)
for(Field fe : entryfds) {
fe.setAccessible(true);
if(fe.getName().toString().equals(enToDto ? dtoMapName : fieldName) ) {//遍歷Entity類的屬性與dto屬性註解中的value值匹配
entFieldType = fe.getGenericType().toString().substring(fe.getGenericType().toString().lastIndexOf(".") + 1); //獲取Entity類屬性類型
dtoFieldName = enToDto ? dtoMapName : fd.getName().toString();
break tohere;
}
}
}
}
}
if(dtoFieldName!= null && !dtoFieldName.equals("null")) {
for(Method md : (enToDto ? entrys : dtos)) {
if(md.getName().toUpperCase().equals("GET"+dtoFieldName.toUpperCase())){
dtoFieldName = null;
if(md.invoke(enToDto ? entry : dto) == null) { break;} //去空操作
//Entity類field 與Dto類field類型不一致通過TypeProcessor處理轉換
value = (entFieldType.equals(dtoFieldType))? md.invoke(enToDto ? entry : dto) :TypeProcessor(entFieldType, dtoFieldType,md.invoke(enToDto ? entry : dto),enToDto ? true : false);
m.invoke(enToDto ? dto : entry, value); //得到field的值 通過invoke()賦值給要轉換類的對應屬性
value = null;
break;
}
}
}
}
}
return enToDto ? dto : entry;
}
//類型轉換處理
public static Object TypeProcessor(String entFieldType,String dtoFieldType, Object obj,boolean enToDto) {
if(entFieldType.equals(dtoFieldType)) return obj;
if(!entFieldType.equals(dtoFieldType)) {
switch(entFieldType) {
case "Date":
return (enToDto)?TypeConverter.dateToString((Date) obj):TypeConverter.stringToDate(obj.toString());
case "Timestamp":
return TypeConverter.timestampToTimestampString((Timestamp)obj);
case "Integer":
return (enToDto) ? obj.toString() : Integer.parseInt((String)obj) ;
}
}
return obj;
}
上面EnAndDtoMapper()方法的實現是Entity和Dto之間互相轉換結合在一起,enToDto = true 表示的是Entity轉Dto實現,false則相反。
3. 如何使用?
1)Entity類 與 Dto類對應
2)調用
public static void main(String[] args) {
//Entity數據轉成Dto數據集
Person person = dao.getPersonRecord();
RelationMapperUtils.entryAndDtoMapper(person,new PersonDto());
//Dto數據轉成Entity數據
RelationMapperUtils.entryAndDtoMapper(new Person(),personDto,false);
}
以上便能自動實現數據的轉換,大量減少get,set的代碼,省事!!!
大家如果還有其他的需求都可以往方法中添加,來達到適合項目的需求,整體下來擴展性算還不錯。