如圖所示,在開發之中,不管是MVC式的三層架構,仍是DDD領域驅動式的架構。總會有各類DTO、DO、PO、VO之間的轉換需求。因此咱們常常會定義兩層Object字段是保持一致的,便於防腐層Assember操做。但現實需求中也會遇到一些複雜映射。因此咱們應該如何基於場景選擇合適的BeanCopy框架呢?java
這篇博客主要整理一下BeanCopy類框架。git
除了HardCopy以外(手寫set get) 經常使用的BeanCopy選擇有如下:github
我直接給出一個performance報告 BeanCopy框架性能對比 結論圖: spring
我主要推兩大類編程
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
/**
* source -> destination
*
* @param car
* @return
*/
@Mappings({
@Mapping(source = "middleName", target = "middle"),
@Mapping(target = "email", ignore = true)
})
PersonDestination sourceToDestination(PersonSource car);
}
複製代碼
編譯以後你會發現target多了一個實現類 緩存
一樣的道理咱們看看Selmabash
@Mapper
public interface SelmaPersonMapper {
SelmaPersonMapper INSTANCE = Selma.mapper(SelmaPersonMapper.class);
/**
* source -> destination
*
* @param car
* @return
* @Maps(withCustomFields = {
* @Field({"middleName", "middle"})
* }, withIgnoreFields = {"email"})
*/
@Maps(withCustomFields = {
@Field({"middleName", "middle"})
}, withIgnoreFields = {"email"})
PersonDestination sourceToDestination(PersonSource car);
}
複製代碼
因此Selma和MapStruct是很是類似的,原理同樣,而且在註解和用法上幾乎同樣,我認爲MapStruct更好的緣由主要是社區更活躍,與SpringBoot集成更好,而且生成的代碼更規範、簡潔、漂亮。
架構
private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
PersonDestination orikaDestination = mapper.map(source, PersonDestination.class);
複製代碼
若是是List互相轉換併發
private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
List<PersonSource> sourceList = Lists.newArrayList(source);
List<PersonDestination> personDestinations = mapper.mapAsList(sourceList, PersonDestination.class);
複製代碼
若是是字段名有映射的app
private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(PersonSource.class, PersonDestination.class)
.field("firstName", "givenName")
.field("lastName", "sirName")
.byDefault()
.register();
MapperFacade mapper = mapperFactory.getMapperFacade();
PersonDestination destination = mapper.map(source, PersonDestination.class);
複製代碼
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class PersonSourceComputer {
private String name;
private BigDecimal price;
}
複製代碼
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class PersonSourceSon {
private String sonName;
private List<PersonSourceComputer> computers;
}
複製代碼
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class PersonSource {
private String firstName;
private String middleName;
private String lastName;
private String email;
List<PersonSourceSon> son;
}
複製代碼
public class BeanCopyTest {
private static final Logger logger = LoggerFactory.getLogger(BeanCopyTest.class);
private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
//static {
// mapperFactory.classMap(PersonSource.class, PersonDestination.class).byDefault().register();
//}
public static void main(String[] args) {
for (int i = 1; i < 11; i++) {
beanCopyTest(i);
}
}
private static void beanCopyTest(int i) {
PersonSource source = initAndGetPersonSource();
Stopwatch stopwatch = Stopwatch.createStarted();
// MapStruct
PersonDestination destination = PersonMapper.INSTANCE.sourceToDestination(source);
System.out.println(destination);
stopwatch.stop();
logger.info("第" + i + "次" + "MapStruct cost:" + stopwatch.toString());
// Selma
stopwatch = Stopwatch.createStarted();
PersonDestination selmaDestination = SelmaPersonMapper.INSTANCE.sourceToDestination(source);
System.out.println(selmaDestination);
stopwatch.stop();
logger.info("第" + i + "次" + "Selma cost:" + stopwatch.toString());
// BeanUtils
stopwatch = Stopwatch.createStarted();
PersonDestination bUtilsDestination = new PersonDestination();
BeanUtils.copyProperties(source, bUtilsDestination);
System.out.println(bUtilsDestination);
stopwatch.stop();
logger.info("第" + i + "次" + "BeanUtils cost:" + stopwatch.toString());
// BeanCopier
stopwatch = Stopwatch.createStarted();
BeanCopier beanCopier = BeanCopier.create(PersonSource.class, PersonDestination.class, false);
PersonDestination bcDestination = new PersonDestination();
beanCopier.copy(source, bcDestination, null);
System.out.println(bcDestination);
stopwatch.stop();
logger.info("第" + i + "次" + "BeanCopier cost:" + stopwatch.toString());
// Orika
stopwatch = Stopwatch.createStarted();
MapperFacade mapper = mapperFactory.getMapperFacade();
PersonDestination orikaDestination = mapper.map(source, PersonDestination.class);
System.out.println(orikaDestination);
stopwatch.stop();
logger.info("第" + i + "次" + "Orika cost:" + stopwatch.toString());
}
private static PersonSource initAndGetPersonSource() {
PersonSource source = new PersonSource();
// set some field values
source.setFirstName("firstName");
source.setMiddleName("middleName");
source.setLastName("lastName");
source.setEmail("email");
source.setSon(Lists.newArrayList(new PersonSourceSon(
"sonName", Lists.newArrayList(new PersonSourceComputer("macBook", BigDecimal.valueOf(15000)))
)));
return source;
}
}
複製代碼
17:56:30.029 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次MapStruct cost:4.179 ms
17:56:30.035 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次Selma cost:2.727 ms
17:56:30.095 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次BeanUtils cost:59.65 ms
17:56:30.139 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次BeanCopier cost:43.52 ms
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次Orika cost:167.1 ms
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次MapStruct cost:88.97 μs
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次Selma cost:36.72 μs
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次BeanUtils cost:68.76 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次BeanCopier cost:62.75 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次Orika cost:108.0 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次MapStruct cost:63.29 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次Selma cost:71.12 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次BeanUtils cost:81.64 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次BeanCopier cost:68.01 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次Orika cost:112.4 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次MapStruct cost:54.27 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次Selma cost:37.97 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次BeanUtils cost:124.3 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次BeanCopier cost:124.9 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次Orika cost:107.3 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次MapStruct cost:43.39 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次Selma cost:50.03 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次BeanUtils cost:75.00 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次BeanCopier cost:50.83 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次Orika cost:92.18 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次MapStruct cost:34.60 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次Selma cost:61.26 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次BeanUtils cost:118.6 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次BeanCopier cost:102.7 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次Orika cost:170.5 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次MapStruct cost:65.29 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次Selma cost:52.06 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次BeanUtils cost:86.51 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次BeanCopier cost:101.3 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次Orika cost:119.0 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次MapStruct cost:56.88 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次Selma cost:35.56 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次BeanUtils cost:98.93 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次BeanCopier cost:69.25 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次Orika cost:95.27 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次MapStruct cost:33.60 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次Selma cost:31.90 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次BeanUtils cost:96.19 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次BeanCopier cost:77.15 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次Orika cost:95.17 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次MapStruct cost:45.55 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Selma cost:32.28 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanUtils cost:62.06 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanCopier cost:46.78 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Orika cost:113.6 μs
複製代碼
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次Orika cost:167.1 ms
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次Orika cost:108.0 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次Orika cost:112.4 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次Orika cost:107.3 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次Orika cost:92.18 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次Orika cost:170.5 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次Orika cost:119.0 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次Orika cost:95.27 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次Orika cost:95.17 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Orika cost:113.6 μs
複製代碼
17:56:30.029 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次MapStruct cost:4.179 ms
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次MapStruct cost:88.97 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次MapStruct cost:63.29 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次MapStruct cost:54.27 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次MapStruct cost:43.39 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次MapStruct cost:34.60 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次MapStruct cost:65.29 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次MapStruct cost:56.88 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次MapStruct cost:33.60 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次MapStruct cost:45.55 μs
複製代碼
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次MapStruct cost:45.55 μs
PersonDestination(firstName=firstName, middle=middleName, lastName=lastName, email=null, son=[PersonDestinationSon(sonName=sonName, computers=[PersonDestinationComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Selma cost:32.28 μs
PersonDestination(firstName=firstName, middle=null, lastName=lastName, email=email, son=[PersonSourceSon(sonName=sonName, computers=[PersonSourceComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanUtils cost:62.06 μs
PersonDestination(firstName=firstName, middle=null, lastName=lastName, email=email, son=[PersonSourceSon(sonName=sonName, computers=[PersonSourceComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanCopier cost:46.78 μs
PersonDestination(firstName=firstName, middle=null, lastName=lastName, email=email, son=[PersonDestinationSon(sonName=sonName, computers=[PersonDestinationComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Orika cost:113.6 μs
複製代碼
1.無論使用實驗中的哪一種框架,在性能上其實絕對值相差不會太大(非第一次運行)。 2.個別框架拷貝後引用是PersonSourceSon,個別是PersonDestinationSon,說明不一樣框架在深淺拷貝方案上實現不一樣。 3.有字段名映射、ignore、格式化等需求時,不一樣框架支持的不一樣。
1.在平常開發中,BeanCopy需求無非是三種
那麼針對以上三點,我認爲
2.關於性能的取捨
咱們經過性能測試能夠發現,一旦運行過一次以後,上面幾種框架單次copy性能絕對值都很是低(個別框架主要基於Asm開始的耗時、緩存原理、jvm熱代碼優化等緣由第一次會久一點)。因此性能取捨上的考慮,主要基於量和系統場景。若是是特別誇張的併發,或者說真的系統到了須要優化類庫提高性能的瓶頸上。這種低絕對值之間的相對差距纔有意義,由於單次之間的差距是微秒級的,若是沒有一個量的乘積放大,是能夠忽略性能上的差別。正常大部分公司是沒有這個需求的,沒有必要追求這種極致的性能,因此考慮的更可能是既處於一個"高性能"表現(絕對值),其它方面讓你很滿意的類庫。