Java的属性拷贝工具类---Mapstruct

Java的属性拷贝工具类



一、BeanUtils.copyProperties的使用场景

涉及到的DO、DTO、VO之间的转换,我们大多使用Spring框架里的BeanUtils.copyProperties来做对象转换,以下举例对比使用工具和不使用工具:

DTO和VO的转换

@Data
public class OrderDTO {
    
    
    private long id;
 
    private Long userId;
 
    private String orderNo;
 
    private Date gmtCreated;
}
@Data
public class OrderVO {
    
    
    private long id;
 
    private long userId;
 
    private String orderNo;
 
    private Date gmtCreated;
}

不使用工具转换:

public static void main(String[] args) {
    
    
    OrderDTO orderDTO = new OrderDTO();
    orderDTO.setId(1L);
    orderDTO.setUserId(123L);
    orderDTO.setOrderNo("20210518000001");
    orderDTO.setGmtCreated(new Date());
 
    OrderVO orderVO = new OrderVO();
    orderVO.setId(orderDTO.getId());
    orderVO.setUserId(orderDTO.getUserId());
    orderVO.setOrderNo(orderDTO.getOrderNo());
    orderVO.setGmtCreated(orderDTO.getGmtCreated());
}

使用工具转换:

OrderVO orderVO = new OrderVO();
BeanUtils.copyProperties(orderDTO, orderVO);
 
//使用自定义工具---如下
OrderVO orderVO=BeanUtil.copyProperties(OrderVO.class, orderDTO);

以下是自定义的一个工具类:

BeanUtil(包装BeanUtils)

package com.test.utils;
 
import java.util.*
import org.springframword.beans.Beanutils;
import org.springframword.util.CollectionUtils;
 
public class BeanUtil extends BeanUtils(
    public BeanUtil(){
    
    
    }
    
    public statci<T, E> List<T> Dto2List(Class<T> destType, List<E> orig){
    
    
        List<T> dest = new ArratList();
        if (CollectionUtils.isEmpty(orig)){
    
    
            return dest;
        }else {
    
    
            Iterator it = orig.iterator();
            while(it.hasNext()){
    
    
                E e = it.next();
                T obj = BeanUtils.instantiateClase(destType);
                BeanUtils.copyProperties(e, obj);
                dest.add(obj);
            }
            return dest;
        }
   }
        
   public statci<T> T copyProperties(Class<T> destType, Object srcObject){
    
    
        if (srcObject == null){
    
    
            return null;
        } else {
    
    
            T obj = BeanUtils.instantiateClase(destType);
            BeanUtils.copyProperties(srcObject, obj);
            return obj;
        }
    }

二、BeanUtils.copyProperties的避坑

使用该方法是一种浅拷贝,会有坑:

  1. 包装类型转基本类型问题

例如OrderDTO的Long类型转换成OrderDO的long时,会抛java.lang.IllegalArgumentException异常

  1. 空格问题

OrderDTO的字段A带前后空格,转换成OrderDO仍然会有空格,造成脏数据。

  1. 查不到引用字段。

三、BeanUtils的替代工具类——Mapstruct

先上结论,Mapstruct的性能远远高于BeanUtils:

在这里插入图片描述

1. Mapstruct 依赖

使用Mapstruct需要依赖的包如下,mapstruct、mapstruct-processor、lombok,可以去仓库中查看最新版本。

<dependency>
	<groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.0.Final</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-processor</artifactId>
	<version>1.5.0.Final</version>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.12</version>
</dependency>

2. Mapstruct的属性拷贝

UserPo和UserEntity的属性类型和名称完全相同。

package mapstruct;
 
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
 
import java.util.Date;
 
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserPo {
    
    
    private Long id;
    private Date gmtCreate;
    private Date createTime;
    private Long buyerId;
    private Long age;
    private String userNick;
    private String userVerified;
}
package mapstruct;
 
import lombok.Data;
 
import java.util.Date;
 
@Data
public class UserEntity {
    
    
    private Long id;
    private Date gmtCreate;
    private Date createTime;
    private Long buyerId;
    private Long age;
    private String userNick;
    private String userVerified;
}

定义mapstruct接口,在接口上打上@Mapper注解。

接口中有一个常量和一个方法,常量的值是接口的实现类,这个实现类是Mapstruct默认帮我们实现的,下文会讲到。定义了一个po2entity的转换方法,表示把入参UserPo对象,转换成UserEntity。注意@Mapper是Mapstruct的注解。

package mapstruct;
 
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
 
@Mapper
public interface IPersonMapper {
    
    
    IPersonMapper INSTANCT = Mappers.getMapper(IPersonMapper.class);
    UserEntity po2entity(UserPo userPo);
}

3. Mapstruct 性能优于 BeanUtils 的原因

Java程序执行的过程,是由编译器先把java文件编译成class字节码文件,然后由JVM去解释执行class文件。Mapstruct正是在java文件到class这一步帮我们实现了转换方法,即做了预处理,提前编译好文件,如果用过lombok的同学一定能理解其好处,通过查看class文件,可以看出IPersonMapper被打上org.mapstruct.Mapper注解后,编译器自动会帮我们生成一个实现类IPersonMapperImpl,并实现了po2entity这个方法。

在这里插入图片描述
BeanUtils转换的原理是使用的反射,反射的效率相对来说是低的,因为jvm优化在这种场景下有可能无效,所以在对性能要求很高或者经常被调用的程序中,尽量不要使用。我们平时在研发过程中,也会遵守这个原则,非必要,不反射


猜你喜欢

转载自blog.csdn.net/weixin_47410172/article/details/127842390