最近开始负责一个数据量比较大的业务模块,要求把相关数据全部查出来,不分页,要组树结构,数据从dao层到service由entity对象到Vo对象给前端展示。那么就涉及到对象拷贝,开始的时候用的Spring的BeanUtils做对象转换,并没有什么问题,后来到了测试那里,加大数据量,发现接口越来越慢,开始以为数据库查询问题,把sql搬到数据库运行,发现并不慢,因为关键字段基本都走了索引,不会很慢,后来一步一步找,发现是BeanUtils耗时引起的,然后就有了下面的关于三种对象拷贝方式的实践
这里可能很多小伙伴只用过Spring的BeanUtils,其余的两种没用过,不过没关系,接下来来个简单的测试
<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> 复制代码
/** * @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"); } } 复制代码
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; } } } 复制代码