引出問題
在日常開發(fā)中鸠蚪,我們經(jīng)常需要給對象進行賦值,通常會調(diào)用其set/get方法,有些時候茅信,如果我們要轉(zhuǎn)換的兩個對象之間屬性大致相同盾舌,會考慮使用屬性拷貝工具進行。
如我們經(jīng)常在代碼中會對一個數(shù)據(jù)結(jié)構(gòu)封裝成DO蘸鲸、SDO妖谴、DTO、VO等酌摇,而這些Bean中的大部分屬性都是一樣的膝舅,所以使用屬性拷貝類工具可以幫助我們節(jié)省大量的set和get操作。
市面上有很多類似的工具類窑多,比較常用的有
1仍稀、Spring BeanUtils?
2、Cglib BeanCopier?
3埂息、Apache BeanUtils?
4技潘、Apache PropertyUtils?
5、Dozer
那么千康,我們到底應(yīng)該選擇哪種工具類更加合適呢崭篡?為什么阿里巴巴Java開發(fā)手冊中提到禁止使用Apache BeanUtils呢?
性能對比
測試代碼:
package com.example.demo.bussniss.entity;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.StopWatch;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
/**
* @author :suyanlong
* @date :Created in 2020/7/30 9:29
* @description:ceshi
* @version: 1.0
*/
public class PersonTest {
// 使用Spring BeanUtils進行屬性拷貝:
? ? private void mappingBySpringBeanUtils(PersonDO personDO, int times) {
StopWatch stopwatch =new StopWatch();
? ? ? ? stopwatch.start();
? ? ? ? for (int i =0; i < times; i++) {
PersonDTO personDTO =new PersonDTO();
? ? ? ? ? ? org.springframework.beans.BeanUtils.copyProperties(personDO, personDTO);
? ? ? ? }
stopwatch.stop();
? ? ? ? System.out.println("spring copy time:" + stopwatch.getTotalTimeMillis());
? ? }
// 使用Cglib BeanCopier進行屬性拷貝:
? ? private void mappingByCglibBeanCopier(PersonDO personDO, int times) {
StopWatch stopwatch =new StopWatch();
? ? ? ? stopwatch.start();
? ? ? ? for (int i =0; i < times; i++) {
PersonDTO personDTO =new PersonDTO();
? ? ? ? ? ? BeanCopier copier = BeanCopier.create(PersonDO.class, PersonDTO.class, false);
? ? ? ? ? ? copier.copy(personDO, personDTO, null);
? ? ? ? }
stopwatch.stop();
? ? ? ? System.out.println("cglibs copy time:" + stopwatch.getTotalTimeMillis());
? ? }
// 使用Apache BeanUtils進行屬性拷貝:
? ? private void mappingByApacheBeanUtils(PersonDO personDO, int times)
throws InvocationTargetException, IllegalAccessException {
StopWatch stopwatch =new StopWatch();
? ? ? ? stopwatch.start();
? ? ? ? for (int i =0; i < times; i++) {
PersonDTO personDTO =new PersonDTO();
? ? ? ? ? ? BeanUtils.copyProperties(personDTO, personDO);
? ? ? ? }
stopwatch.stop();
? ? ? ? System.out.println("apbean copy time:" + stopwatch.getTotalTimeMillis());
? ? }
// 使用Apache PropertyUtils進行屬性拷貝:
? ? private void mappingByApachePropertyUtils(PersonDO personDO, int times)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
StopWatch stopwatch =new StopWatch();
? ? ? ? stopwatch.start();
? ? ? ? for (int i =0; i < times; i++) {
PersonDTO personDTO =new PersonDTO();
? ? ? ? ? ? PropertyUtils.copyProperties(personDTO, personDO);
? ? ? ? }
stopwatch.stop();
? ? ? ? System.out.println("approp copy time:" + stopwatch.getTotalTimeMillis());
? ? }
public static void main(String[] args)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
PersonDO personDO =new PersonDO();
? ? ? ? personDO.setName("Hollis");
? ? ? ? personDO.setAge(26);
? ? ? ? personDO.setBirthday(new Date());
? ? ? ? personDO.setId(1);
? ? ? ? PersonTest mapperTest =new PersonTest();
? ? ? ? mapperTest.mappingBySpringBeanUtils(personDO, 100);
? ? ? ? mapperTest.mappingBySpringBeanUtils(personDO, 1000);
? ? ? ? mapperTest.mappingBySpringBeanUtils(personDO, 10000);
? ? ? ? mapperTest.mappingBySpringBeanUtils(personDO, 100000);
? ? ? ? mapperTest.mappingBySpringBeanUtils(personDO, 1000000);
? ? ? ? mapperTest.mappingByCglibBeanCopier(personDO, 100);
? ? ? ? mapperTest.mappingByCglibBeanCopier(personDO, 1000);
? ? ? ? mapperTest.mappingByCglibBeanCopier(personDO, 10000);
? ? ? ? mapperTest.mappingByCglibBeanCopier(personDO, 100000);
? ? ? ? mapperTest.mappingByCglibBeanCopier(personDO, 1000000);
? ? ? ? mapperTest.mappingByApachePropertyUtils(personDO, 100);
? ? ? ? mapperTest.mappingByApachePropertyUtils(personDO, 1000);
? ? ? ? mapperTest.mappingByApachePropertyUtils(personDO, 10000);
? ? ? ? mapperTest.mappingByApachePropertyUtils(personDO, 100000);
? ? ? ? mapperTest.mappingByApachePropertyUtils(personDO, 1000000);
? ? ? ? mapperTest.mappingByApacheBeanUtils(personDO, 100);
? ? ? ? mapperTest.mappingByApacheBeanUtils(personDO, 1000);
? ? ? ? mapperTest.mappingByApacheBeanUtils(personDO, 10000);
? ? ? ? mapperTest.mappingByApacheBeanUtils(personDO, 100000);
? ? ? ? mapperTest.mappingByApacheBeanUtils(personDO, 1000000);
? ? }
}
測試結(jié)果
綜上吧秕,我們基本可以得出結(jié)論琉闪,在性能方面,Spring BeanUtils和Cglib BeanCopier表現(xiàn)比較不錯砸彬,而Apache PropertyUtils颠毙、Apache BeanUtils以及Dozer則表現(xiàn)的很不好。
所以砂碉,如果考慮性能情況的話蛀蜜,建議大家不要選擇Apache PropertyUtils、Apache BeanUtils以及Dozer等工具類增蹭。
很多人會不理解滴某,為什么大名鼎鼎的Apache開源出來的的類庫性能確不高呢?這不像是Apache的風(fēng)格呀滋迈,這背后導(dǎo)致性能低下的原因又是什么呢霎奢?
其實,是因為Apache BeanUtils力求做得完美, 在代碼中增加了非常多的校驗饼灿、兼容幕侠、日志打印等代碼,過度的包裝導(dǎo)致性能下降嚴(yán)重碍彭。
總結(jié)
本文通過對比幾種常見的屬性拷貝的類庫晤硕,分析得出了這些工具類的性能情況悼潭,最終也驗證了《阿里巴巴Java開發(fā)手冊》中提到的"Apache BeanUtils 效率低"的事實。
但是本文只是站在性能這一單一角度進行了對比舞箍,我們在選擇一個工具類的時候還會有其他方面的考慮舰褪,比如使用成本、理解難度疏橄、兼容性抵知、可擴展性等,對于這種拷貝類工具類软族,我們還會考慮其功能是否完善等。
就像雖然Dozer性能比較差残制,但是他可以很好的和Spring結(jié)合立砸,可以通過配置文件等進行屬性之間的映射等,也受到了很多開發(fā)者的喜愛初茶。
深拷貝颗祝、淺拷貝參考:https://blog.csdn.net/weixin_39585051/article/details/107681667