你还在用BeanUtils做对象拷贝吗?

最近开始负责一个数据量比较大的业务模块,要求把相关数据全部查出来,不分页,要组树结构,数据从dao层到service由entity对象到Vo对象给前端展示。那么就涉及到对象拷贝,开始的时候用的Spring的BeanUtils做对象转换,并没有什么问题,后来到了测试那里,加大数据量,发现接口越来越慢,开始以为数据库查询问题,把sql搬到数据库运行,发现并不慢,因为关键字段基本都走了索引,不会很慢,后来一步一步找,发现是BeanUtils耗时引起的,然后就有了下面的关于三种对象拷贝方式的实践

实践:Apache的BeanUtils、Spring的BeanUtils、Mapstruct

这里可能很多小伙伴只用过Spring的BeanUtils,其余的两种没用过,不过没关系,接下来来个简单的测试

  • 引入maven依赖,为了测试方便这里直接创建的SpringBoot项目,用的junit测试
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Spring的BeanUtils -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- Apache的BeanUtils依赖 -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
        </dependency>
        <!-- mapstruct依赖 -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
    </dependencies>

复制代码
  • 类结构
你还在用BeanUtils做对象拷贝吗?
  • 4个简单类
/**
 * @description user 实体
 * @author Wanm
 * @date 2020/7/8 21:37
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String id;

    private String name;

    private Integer age;

    private String address;

    private String sex;

}

/**
 * @description userVo
 * @author Wanm
 * @date 2020/7/8 21:37
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVo {

    private String id;

    private String name;

    private Integer age;

    private String address;

    private String sex;

}


/**
 * @description UserTransfer
 * @author Wanm
 * @date 2020/7/8 21:37
 */
@Mapper
public interface UserTransfer {

    /**
     * entity转vo
     * @param user
     * @return
     */
    List<UserVo> entityToVo(List<User> user);
}


/**
 * 测试类
 */
@SpringBootTest
class MapstructApplicationTests {


    @Test
    void contextLoads() {
        //这里拿100w数据做数据初始化
        List<User> userList = new ArrayList<User>();
        for (int i = 0; i < 1000000; i++) {
            User user = new User(UUID.randomUUID().toString(),UUID.randomUUID().toString(),i,UUID.randomUUID().toString(),UUID.randomUUID().toString());
            userList.add(user);
        }
        System.out.println(userList.get(0));
        System.out.println("开始拷贝---------------------------------------");
        testBeanUtils(userList);
        testSpringBeanUtils(userList);
        testMapStruct(userList);
    }

    /**
     *  Apache的BeanUtils
     * @param userList
     */
    public static void testBeanUtils(List<User> userList){
        long start = System.currentTimeMillis();
        List<UserVo> userVos = new ArrayList<>();
        userList.forEach(item->{
            UserVo userVo  = new UserVo();
            try {
                BeanUtils.copyProperties(userVo,item);
                userVos.add(userVo);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        long end = System.currentTimeMillis();
        System.out.println(userVos.get(0));
        System.out.println("集合大小参数验证"+userVos.size()+"Apache的BeanUtils耗时:"+(end-start)+"ms");
    }

    /**
     * Spring的BeanUtils
     * @param userList
     */
    public static void testSpringBeanUtils(List<User> userList){
        long start = System.currentTimeMillis();
        List<UserVo> userVos = new ArrayList<>();
        userList.forEach(item->{
            UserVo userVo  = new UserVo();
            try {
                org.springframework.beans.BeanUtils.copyProperties(item,userVo);
                userVos.add(userVo);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        long end = System.currentTimeMillis();
        System.out.println(userVos.get(0));
        System.out.println("集合大小参数验证"+userVos.size()+"Spring的BeanUtils耗时:"+(end-start)+"ms");
    }

    /**
     * mapStruct拷贝
     * @param userList
     */
    public  void testMapStruct(List<User> userList){
        long start = System.currentTimeMillis();
        List<UserVo> userVos = Mappers.getMapper(UserTransfer.class).entityToVo(userList);
        long end = System.currentTimeMillis();
        System.out.println(userVos.get(0));
        System.out.println("集合大小参数验证"+userVos.size()+"mapStruct耗时:"+(end-start)+"ms");
    }

}
复制代码
  • 实际开发中vo类属性字段会比实体类少很多。这里只做测试,下面看测试结果
你还在用BeanUtils做对象拷贝吗?
  • 有没有看到MapStruct的耗时41ms,完胜有木有,数据量越大越能看到差异,下图是针对不同数据量的耗时做具体分析:
Apache Spring MapStruct
1000 67ms 10ms 2ms
1w 174ms 35ms 3ms
10w 808ms 69ms 9ms
100w 5620ms 336ms 42ms

由此可以看出数据量越大MapStruct>Spring>Apache,这个性能优势越来越明显,日常开发中对象拷贝只是代码中的一小部分逻辑,如果数据量大的话还是建议大家使用MapStruct的方式,提高接口的性能。数据量不大的话Spring的BeanUtils也行,还是看实际业务场景!!!

原理:MapStruct使用注解处理器生成实现类,实现类内部是原生的new对象,然后SetXxx/getXxx方式赋值进行数据拷贝的,类似lombok,看实现类的.class

public class UserTransferImpl implements UserTransfer {
    public UserTransferImpl() {
    }

    public List<UserVo> entityToVo(List<User> user) {
        if (user == null) {
            return null;
        } else {
            List<UserVo> list = new ArrayList(user.size());
            Iterator var3 = user.iterator();

            while(var3.hasNext()) {
                User user1 = (User)var3.next();
                list.add(this.userToUserVo(user1));
            }

            return list;
        }
    }

    protected UserVo userToUserVo(User user) {
        if (user == null) {
            return null;
        } else {
            UserVo userVo = new UserVo();
            userVo.setId(user.getId());
            userVo.setName(user.getName());
            userVo.setAge(user.getAge());
            userVo.setAddress(user.getAddress());
            userVo.setSex(user.getSex());
            return userVo;
        }
    }
}
复制代码

原文 

https://juejin.im/post/5f05d26df265da23020a847c

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 你还在用BeanUtils做对象拷贝吗?

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址