原创

对象映射框架MapStruct对比orika

1、各大对象映射框架性能对比

工具 实现方式 缺点 说明
mapstruct getter/setter方法 需要了解注解和配置项语法 JSR269注解处理器在编译期自动生成Java
Bean转换代码,支持可配置化,扩展性强
orika 动态生成字节码 首次调用耗时较久,性能适中 采用javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件
Spring BeanUtils 反射机制 不支持名称相同但类型不同的属性转换
Apache BeanUtils 反射机制 需要处理编译期异常,性能最差
dozer 反射机制 性能差 使用reflect包下Field类的set(Object
obj, Object value)方法进行属性赋值
BeanCopier 反射机制 \1. BeanCopier只拷贝名称和类型都相同的属性。即便基本类型与其对应的包装类型也不能相互转换; 使用ASM的MethodVisitor直接编写各属性的get/set方法

就性能而言:mapstruct性能无疑是是最高的,接下来依次是Spring BeanUtils>orika>BeanCopier>dozer>apache BeanUtils

2、MapStruct的使用

2.1、引入pom

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.3.0.Final</version>
    <scope>provided</scope>
</dependency>

2.2 、单个bean映射

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")
public interface OrderConvert {

    @Mapping(source = "id", target = "orderId")
    @Mapping(source = "createTime", target = "orderTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    OrderDTO from(Order order);

}

测试代码

@Test
public void test(
{
    Order order = Order.builder()
        .id(123L)
        .buyerPhone("13707318123")
        .buyerAddress("中电软件园")
        .amount(10000L)
        .payStatus(1)
        .createTime(LocalDateTime.now())
        .build();

    OrderConvert orderConvert = Mappers.getMapper(OrderConvert.class);
    OrderDTO orderDTO = orderConvert.from(order);

    System.out.println("order:    " + order);
    System.out.println("orderDTO: " + orderDTO);
}

2.3、多个bean的映射

@Mapper(componentModel = "spring")
public interface GoodInfoConvert {

    /** Long => String 隐式类型转换 */
    @Mapping(source = "good.id", target = "goodId")
    /** 属性名不同, */
    @Mapping(source = "type.name", target = "typeName")
    /** 属性名不同 */
    @Mapping(source = "good.title", target = "goodName")
    /** 属性名不同 */
    @Mapping(source = "good.price", target = "goodPrice")
    GoodInfoDTO from(GoodInfo good, GoodType type);

}

2.4、参数的含义映射

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Mapper(imports = {CustomMapping.class})
public interface StudentConvert {

    @Mapping(source = "id", target = "studentId")
    @Mapping(source = "name", target = "studentName")
    @Mapping(source = "age", target = "age")
    @Mapping(target = "ageLevel", expression = "java(CustomMapping.ageLevel(student.getAge()))")
    @Mapping(target = "sexName", expression = "java(CustomMapping.sexName(student.getSex()))")
    @Mapping(source = "admissionTime", target = "admissionDate", dateFormat = "yyyy-MM-dd")
    StudentDTO from(Student student);

    default LocalDate map(LocalDateTime time) {
        return time.toLocalDate();
    }

}

自定义的映射类

public class CustomMapping {

    static final String[] SEX = {"女""男""未知"};

    public static String sexName(Integer sex) {

        if (sex < 0 && sex > 2){
            throw new IllegalArgumentException("invalid sex: " + sex);
        }
        return SEX[sex];
    }

    public static String ageLevel(Integer age) {
        if (age < 18) {
            return "少年";
        } else if (age >= 18 && age < 30) {
            return "青年";
        } else if (age >= 30 && age < 60) {
            return "中年";
        } else {
            return "老年";
        }
    }

}

3、orika的使用

3.1 引入pom

<dependency>
    <groupId>ma.glasnost.orika</groupId>
    <artifactId>orika-core</artifactId>
    <version>1.5.2</version>
</dependency>

3.2、初始化实例

package tech.chenxing.deploy.configuration;

import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MapperConfig {
    @Bean
    public MapperFactory mapperFactory() {
        return new DefaultMapperFactory.Builder().build();
    }
}

3.3、配置映射对象

定义转换器

public class FlowDocDOConverter extends CustomMapper<FlowDocFlowDocBean{
    @Autowired private DocInfoMapperExt docInfoMapperExt;

    @Override
    public void mapAtoB(FlowDoc flowDocDO, FlowDocBean flowDocBean, MappingContext context) {
        if (StringUtils.isNotBlank(flowDocDO.getDocUuid())) {

            DocInfo docInfoDO = docInfoMapperExt.getByUUID(flowDocDO.getDocUuid());
            ValidationUtil.assertNull(
                    docInfoDO,
                    new BizFlowManagerException(
                            BizFlowManagerBaseResultCodeEnum.FLOW_NOT_EXISTS,
                            flowDocDO.getDocUuid()));
            flowDocBean.setId(docInfoDO.getId());
            flowDocBean.setFileId(docInfoDO.getFileId());
            flowDocBean.setName(docInfoDO.getName());
            flowDocBean.setDocPassword(docInfoDO.getDocPassword());
            flowDocBean.setDocSource(docInfoDO.getDocSource());
            flowDocBean.setEncryption(docInfoDO.getEncryption());
            flowDocBean.setGmtCreate(flowDocDO.getGmtCreate());
            flowDocBean.setGmtModified(flowDocDO.getGmtModified());
        }
    }
}

注入转换器

package tech.chenxing.deploy.configuration;

import ma.glasnost.orika.MapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;


@Component
public class MapperInit {
    @Autowired private MapperFactory mapperFactory;
    @Autowired private FlowDocDOConverter flowDocDOConverter;


    @PostConstruct
    public void init() {
        // 注册文档映射方式
        mapperFactory
                .classMap(User.class, UserDTO.class)
                .field("docUuid""docId")
                .byDefault() // 剩余的字段映射
                .customize(flowDocDOConverter)
                .register();
    }
}

代码使用

public xxx queryuser() {
        List<User> userList =
                userMapperExt.queryUser();
        List<UserDTO> userDTOList =
                mapperFactory.getMapperFacade().mapAsList(userList, UserDTO.class);
        QueryFlowDocResult queryFlowDocResult = new QueryFlowDocResult();
    }
正文到此结束
本文目录