比BeanUtils更好用的MapStruct

119 阅读2分钟

MapStruct

MapStruct是一个Java工具,它可以自动生成对象之间的映射代码,比如PO、DTO、VO等。MapStruct的作用是简化对象转换的过程,提高代码的可读性和性能,避免手动编写大量的get、set方法。

使用MapStruct的原因是它比BeanUtils等反射方式的工具有很多优势,例如:

  • MapStruct是在编译期生成映射代码,而BeanUtils是在运行期通过反射机制获取属性和赋值,因此MapStruct的性能更高,反射会带来额外的开销和性能损耗
  • MapStruct生成的代码是普通的Java代码,可以直接查看和修改,而BeanUtils生成的代码是动态的,不易阅读和理解,也不利于重构和维护
  • MapStruct支持不同类型之间的自动转换,比如String和Date,而BeanUtils需要手动处理类型转换的逻辑
  • MapStruct支持自定义映射规则,比如忽略某些属性,或者使用表达式或方法来映射属性,而BeanUtils只能按照属性名进行映射

如何使用MapStruct

  • 导入依赖
<mapstruct.version>1.5.3.Final</mapstruct.version>

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${mapstruct.version}</version>
</dependency>

<!--生成實現類依賴-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${mapstruct.version}</version>
</dependency>
  • 使用
@Data
public class TestDo {

    private String id;

    private String name;

    private String phone;

    private String password;

    private String nickname;

    private String avatar;

    private String age;

    private LocalDateTime createTime;

}

@Data
public class TestVo {

    private String id;

    private String userName;

    private int age;

    private String createTime;

    private List<TestDto> testDtoList;

}

// 此處 mapper 注意別引用錯了 mapcstruct 的 mapper
@Mapper
public interface TestConvert {

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

    TestDo convert(TestVo testVo);

    // 如果字段名稱不同,可以用@Mapping來指定
    @Mappings({
            @Mapping(source = "name", target = "userName")
    })
    TestVo convert(TestDo testDo);

    List<TestVo> convert(List<TestDo> testDoList);

}
  • 编译之后就会生成一个转换实现类
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-12-01T15:41:53+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 11.0.17 (Amazon.com Inc.)"
)
public class TestConvertImpl implements TestConvert {

    @Override
    public TestDo convert(TestVo testVo) {
        if ( testVo == null ) {
            return null;
        }

        TestDo testDo = new TestDo();

        testDo.setId( testVo.getId() );
        // 这里类型不用 自动进行了转换
        testDo.setAge( String.valueOf( testVo.getAge() ) );
        // 日期格式也自动转换 不用手动转换
        if ( testVo.getCreateTime() != null ) {
            testDo.setCreateTime( LocalDateTime.parse( testVo.getCreateTime() ) );
        }

        return testDo;
    }

    @Override
    public TestVo convert(TestDo testDo) {
        if ( testDo == null ) {
            return null;
        }

        TestVo testVo = new TestVo();

        // 自动名称不一样
        testVo.setUserName( testDo.getName() );
        testVo.setId( testDo.getId() );
        if ( testDo.getAge() != null ) {
            testVo.setAge( Integer.parseInt( testDo.getAge() ) );
        }
        if ( testDo.getCreateTime() != null ) {
            testVo.setCreateTime( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( testDo.getCreateTime() ) );
        }

        return testVo;
    }

    @Override
    public List<TestVo> convert(List<TestDo> testDoList) {
        if ( testDoList == null ) {
            return null;
        }

        List<TestVo> list = new ArrayList<TestVo>( testDoList.size() );
        for ( TestDo testDo : testDoList ) {
            list.add( convert( testDo ) );
        }

        return list;
    }
}
  • 具体使用
@RestController
@RequestMapping("/mapstruct")
public class MapstructController {

    @GetMapping("test")
    public ResultForm convertEntity() {
        TestDo testDo = new TestDo().setId("1").setName("zhangsan").setAge("12").setCreateTime(LocalDateTime.now());
        TestVo testVo = TestConvert.INSTANCE.convert(testDo);
        System.out.println("=========");
        return success(testVo);
    }
}


// 返回
 {
  "content": {
    "id": "1",
    "userName": "zhangsan",
    "age": 12,
    "createTime": "2023-12-01T15:45:02.099354"
  },
  "success": true,
  "message": "请求成功",
  "code": 200
}