对象转换工具-MapStruct

  1. MapStruct
    1. 如何接入MapStruct
    2. Java Bean属性拷贝对比
    3. demo
    4. 实现原理

MapStruct

MapStruct是一个代码生成器,它基于约定优于配置方法极大地简化了Java bean类型之间映射的实现。
生成的映射代码使用普通方法调用,因此快速,类型安全且易于理解。

如何接入MapStruct

IDEA Support: https://plugins.jetbrains.com/plugin/10036-mapstruct-support/versions

Java Bean属性拷贝对比

  • 性能
get/set >= [MapStruct](http://mapstruct.org/) > [JMapper](https://jmapper-framework.github.io/jmapper-core/)  >  ["beanCopier(cglib)"](https://github.com/cglib/cglib/blob/master/cglib/src/main/java/net/sf/cglib/beans/BeanCopier.java) > Orika > ModelMapper > Spring BeanUtils > Dozer > Apache BeanUtils
  • 性能对比数据来源:

  • 对比结果:

    • get/set 需要手动编写大量转换代码,代码简洁性差、重复性高和工作量大。
    • beanCopier 性能较高,属性名和类型有较高的匹配要求。
    • MapStruct 性能较高,在编译阶段,生成Get/Set代码,支持不同属性之间自定义转换。
    • Orika 性能较高,支持自定义属性拷贝,性能略差与前两种,但比后面几种高很多。属性名相同单类型不同事需编写转换规则,否则会报错
    • Spring BeanUtils 性能一般,只能支持相关名称的拷贝。
    • Dozer 性能差,使用简单,编写xml不方便。
    • Apache BeanUtils 性能差。

demo

实例代码:

/**
* 定义对象之间转换Mapper
 * @Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口
 *     @Mapper
 *     componentModel :主要是指定实现类的类型,
 *         - default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
 *         -  spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入
 *     uses 使用用户自定义转换器
 *
 *     http://mapstruct.org/documentation/stable/reference/html/
 */
//@Mapper(componentModel = "spring",uses = {DateHandWritten.class})
@Mapper(
        uses = {DateHandWritten.class, UserNameHandWritten.class},
        imports = {LocalDateUtil.class}
        )
public interface PersonMapper {

    PersonMapper  INSTANCE  = Mappers.getMapper(PersonMapper.class);

    @Mappings({
            //@Mapping(source = "name",target = "name",ignore = true),
            @Mapping(target = "birthExpressionFormat", expression = "java(LocalDateUtil.getDateNow().toString())"),
            @Mapping(source = "name",target = "address.name"),
            @Mapping(source = "price",target = "price",numberFormat = "#.00"),
            @Mapping(source = "birthDate",target = "birthDateFormat",dateFormat = "yyyy-MM-dd HH:mm:ss")
    })
    /***
     * @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
     *     source:源属性
     *     target:目标属性
     *     dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat              的日期格式
     *     ignore: 忽略这个字段
     * @Mappings:配置多个@Mapping
     */
    PersonDto toDto(Person person);

    @InheritConfiguration(name = "toDto")
    List<PersonDto> toDtos(List<Person> person);


    /*@InheritInverseConfiguration()
    PersonDto fromDto(Person person);*/

    /**
     * @InheritConfiguration 用于继承配置
     * */
  /*  @InheritConfiguration(name = "toDto")
    void update(Person person, @MappingTarget PersonDto personDto);*/
}

/**定义自定义转换规则*/
public class DateHandWritten {

    public String asString(Date date) {
        return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
            .format( date ) : null;
    }

    public Date asDate(String date) {
        try {
            return date != null ? new SimpleDateFormat( "yyyy-MM-dd" )
                .parse( date ) : null;
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }
    }
}

/**定义自定义转换规则*/
public class UserNameHandWritten {

    public String asUsername(String username) {
        return  "被修改后的name";
    } 
}

/***使用实例*/
public class MapStructTest {

    @Test
    public void personTest(){
        Person person = buildPerson();
        PersonDto personDto = PersonMapper.INSTANCE.toDto(person);
        System.out.println(JSON.toJSONString(personDto));
        /**
        * {"address":{"name":"被修改后的name"},"addresses":[{"name":"被修改后的name"}],"age":0,"birthDate":1555573411245,"birthDateFormat":"2019-04-18","birthExpressionFormat":"Thu Apr 18 15:43:31 CST 2019","name":"被修改后的name","price":"2.35"}
        * */
    }

    @Test
    public void personListTest(){
        Person person = buildPerson();
        List<PersonDto> personDtos = PersonMapper.INSTANCE.toDtos(Lists.newArrayList(person));
        System.out.println(JSON.toJSONString(personDtos));
        /**
        * [{"address":{"name":"被修改后的name"},"addresses":[{"name":"被修改后的name"}],"age":0,"birthDate":1555573439123,"birthDateFormat":"2019-04-18","birthExpressionFormat":"Thu Apr 18 15:43:59 CST 2019","name":"被修改后的name","price":"2.35"}]
        * */
    }

    private Person buildPerson(){
        Address a = new Address();
        a.setName("demo");
        return Person.builder().birthDate(new Date()).price(BigDecimal.valueOf(2.347)).name("中国人").addresses(Lists.newArrayList(a)).build();
    }
}

实现原理

MapStruct是基于JSR 269的Java注解处理器,因此可以在命令行构建中使用(javac、Ant、Maven等等),也可以在IDE内使用。
JSR269规范: 允许在编译期处理注解,读取、修改、添加抽象语法树中的内容。
在Maven 编译阶段将自动实现PersonMapper的对象属性转换接口,生成PersonMapperImpl文件。

文章标题:对象转换工具-MapStruct

字数:1.1k

本文作者:imfan

发布时间:2020-10-02, 15:51:46

最后更新:2022-05-19, 11:16:26

原始链接:https://wiki.aistart.cc/2020/10/02/backend/java/utils/convert/map-struct/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏