还用 BeanUtils 拷贝对象?MapStruct 才是王者!


BeanUtils

因为是 Spring 自带的拷贝功能,所以出境率比较的高;但是在实际使用 BeanUtils 过程中,你是否遇到以下的一些小问题:

属性类型不一样,无法进行拷贝,如数据库中查出来的Date,想转换成时间戳返回给前端;不好意思!不行!另外处理;
只想拷贝部分字段,但是没办法忽略;对不起,不管三七二十一,一顿拷贝;完了再特殊处理;
无法对属性进行规则转换;比如数据库中查询出来的01想在转换成VO之后变成truefalse;sorry!不支持,自行搞定;
性能低

虽然基础的拷贝功能可以做到,但是总觉得跟个糙汉子一样;很多细节都没有做处理,只能单独再做二次加工;


MapStruct

既然 BeanUtils 各种别扭,那有没有更好的方式可以解决这些问题呢?

当然是有的;

那就是今天要详细介绍的对象拷贝的王者:MapStruct

上面说的这些问题,通通都能解决了;

上面把 BeanUtils 比作糙汉子,那 MapStruct 就可以称之为大家闺秀,心细如发,开发过程中能遇到的问题,他都给出了解决方案,完美帮你解决。

1MapStruct

什么是 MapStruct?

MapStruct 是一个代码生成器,它基于约定优于配置方法,极大地简化了 Java bean 类型之间映射的实现。

生成的映射代码使用简单的方法调用,因此速度快、类型安全且易于理解。 ---- 来源于官网

性能

以下是Java各种拷贝方式的耗时对比:
在这里插入图片描述

MapStruct的优点

相比于手动get、set
无需手写转换工具类,减轻大量的体力活

相比与其他动态映射

效率高

核心的转换逻辑并不是通过反射实现,而是通过编译时自动生成基于 getter/setter 转换实现类;

性能高

基于简单的get、set操作,效率达到最佳

编译时类型安全

只能映射相同名称或带映射标记的属性;

编译时产生错误报告

如果映射不完整或映射不正确则会在编译时抛出异常,代码将无法正常运行;

能明确查看转换的细节

编译生成的class对象可以看到详细的转换过程,方便快速定位转换过程中的问题。

MapStruct 常用的重要注解 :

@Mapper

标记这个接口作为一个映射接口,并且是编译时 MapStruct 处理器的入口

@Mapping

解决源对象和目标对象中,属性名字不同的情况

@Mappings

当存在多个 @Mapping 需要配置;可以通过 @Mappings 批量指定

Mappers.getMapper

Mapperclass 获取自动生成的实现对象,从而让客户端可以访问 Mapper 接口的实现

2使用

测试代码

https://github.com/mbb2100/mapstruct-demo

准备

依赖

最新的版本可以通过下面的链接查看

https://mvnrepository.com/artifact/org.MapStruct/MapStruct-jdk8

https://mvnrepository.com/artifact/org.MapStruct/MapStruct-processor
<properties>
     <MapStruct.version>1.3.1.Final</MapStruct.version>
</properties>
<!-- https://mvnrepository.com/artifact/org.MapStruct/MapStruct-jdk8 -->
<dependency>
     <groupId>org.MapStruct</groupId>
     <artifactId>MapStruct-jdk8</artifactId>
     <version>${MapStruct.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.MapStruct/MapStruct-processor -->
<dependency>
     <groupId>org.MapStruct</groupId>
     <artifactId>MapStructrocessor</artifactId>
     <version>${MapStruct.version}</version>
</dependency>

<!-- 非必须 注意:版本过高可能造成对象无法生成-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.22</version>
</dependency>

基础测试对象

@Data
@Builder
@ToString
public class UserDTO {
    private String name;

    private Integer age;

    private Date createTime;
}
@Data
@ToString
public class UserVO {
    private String name;

    private Integer age;

    private Date createTime;
}
@Data
@ToString
public class UserVO1 {
    private String name;

    private Integer age;
    
 // 类型和VO对象不同
    private String createTime;
}

BeanUtils拷贝演示

简单的演示一下BeanUtils拷贝

public class t1 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .build();

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDTO,userVO);
        System.out.println(userVO);

        UserVO1 userVO1 = new UserVO1();
        BeanUtils.copyProperties(userDTO,userVO1);
        System.out.println(userVO1);
    }
}

在这里插入图片描述
可以看到,文章一开始说的问题,就慢慢在暴露了

MapStruct基本功能演示

第一步,定义Mapper

// spring方式加载
@Mapper(componentModel = "spring")
public interface UserMapper {
    // default方式加载
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    /**
     * 将DTO转VO
     *
     * @param userDTO
     * @return
     */
    UserVO userVO2UserDTO(UserDTO userDTO);
}

componentModel 属性用于指定自动生成的接口实现类的组件类型,这个属性支持四个值:

default: 这是默认的情况;通过ClassLoader加载
spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring@Autowired方式进行注入
jsr330: 生成的实现类上会添加@javax.inject.Named@Singleton注解,可以通过 @Inject注解获取
cdi: 生成的映射器是一个应用程序范围的CDI bean,可以通过检索 @Inject

第二步,测试default和spring方式

default

UserDTO userDTO = UserDTO.builder()
        .name("张三")
        .age(10)
        .createTime(new Date())
        .build();
UserVO userVO = UserMapper.INSTANCE.userVO2UserDTO(userDTO);
System.out.println(userVO);

在这里插入图片描述

自动生成的UserMapperImpl.class

public class UserMapperImpl implements UserMapper {
    public UserMapperImpl() {
    }

    public UserVO userVO2UserDTO(UserDTO userDTO) {
        if (userDTO == null) {
            return null;
        } else {
            UserVO userVO = new UserVO();
            userVO.setName(userDTO.getName());
            userVO.setAge(userDTO.getAge());
            userVO.setCreateTime(userDTO.getCreateTime());
            return userVO;
        }
    }
}

spring方式

@Autowired
UserMapper userMapper;
    
@Test
void springTest() {
    UserDTO userDTO = UserDTO.builder()
            .name("张三")
            .age(10)
            .createTime(new Date())
            .build();
    UserVO userVO = userMapper.userVO2UserDTO(userDTO);
    System.out.println(userVO);
}

自动生成的UserMapperImpl.class

@Component
public class UserMapperImpl implements UserMapper {
    public UserMapperImpl() {
    }

    public UserVO userVO2UserDTO(UserDTO userDTO) {
        if (userDTO == null) {
            return null;
        } else {
            UserVO userVO = new UserVO();
            userVO.setName(userDTO.getName());
            userVO.setAge(userDTO.getAge());
            userVO.setCreateTime(userDTO.getCreateTime());
            return userVO;
        }
    }
}

可以看出实现类上面自动加上了 @Component,就可以通过 Spring 的方式注入对象并使用;

进一步封装

上面简单测试可以发现,需要做两个对象的转换,就得定义一个接口和数个互转的方法;

为了不用每次都去写那些重复的转换方法,这里对转换接口再向上做一次抽象;

定义基础的转换接口

包含了最基本的4种转换方式

/**
 * 基础的对象转换Mapper
 *
 * @param <SOURCE> 源对象
 * @param <TARGET> 目标对象
 */
public interface BaseMapper<SOURCE, TARGET> {
    TARGET to(SOURCE var1);

    List<TARGET> to(List<SOURCE> var1);


    SOURCE from(TARGET var1);

    List<SOURCE> from(List<TARGET> var1);
}

修改UserMapper

@Mapper(componentModel = "spring")
public interface UserMapper extends BaseMapper<UserDTO, UserVO> {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
}

测试

使用方式没有产生任何变化

// default
UserVO userVO = UserMapper.INSTANCE.to(userDTO);

// spring
UserVO userVO = userMapper.to(userDTO);

3特殊场景

基础的场景已经会用了,但是往往实际的开发中,会面临各种各样奇奇怪怪的转换,这里就详细的列举一下各种特殊的情况。

日期转换

比如数据库中的日期对象 Date 需要转换成 yyyyMMdd 这样格式的对象

测试对象

@Data
@ToString
public class UserVO1 {
    private String name;

    private Integer age;
    
 // 类型和VO对象不同
    private String createTime;
}

Mapper定义

@Mapper
public interface User1Mapper extends BaseMapper<UserDTO, UserVO1>{
    User1Mapper INSTANCE = Mappers.getMapper(User1Mapper.class);

    @Mappings({
            @Mapping(source = "createTime",target = "createTime",dateFormat = "yyyyMMdd")
    })
    @Override
    UserVO1 to(UserDTO var1);
}

测试

public class t2 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .build();

        UserVO1 userVO1 = User1Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO1);

        List<UserDTO> userDTOS = new ArrayList<>();
        userDTOS.add(userDTO);
        List<UserVO1> userVO1s = User1Mapper.INSTANCE.to(userDTOS);
        System.out.println(userVO1s);
    }
}

在这里插入图片描述

忽略指定字段

部分字段不进行拷贝操作;忽略主要是在Mapper的地方进行配置;

测试对象

采用 UserDTO 和 UserVO1 进行测试

Mapper

@Mapper
public interface User4Mapper extends BaseMapper<UserDTO, UserVO1>{
    User4Mapper INSTANCE = Mappers.getMapper(User4Mapper.class);

    @Mappings({
            // 要忽略的字段
            @Mapping(target = "createTime",ignore = true)
    })
    @Override
    UserVO1 to(UserDTO var1);
}

测试

public class t4 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .build();

        UserVO1 userVO1 = User4Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO1);
    }
}
多数据源拷贝

多个数据源对象的数据拷贝到一个对象中

测试对象

// UserDTO 略...

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class AddressDTO {
    private String country;

    private String province;

    private String city;
}

Mapper

@Mapper(componentModel = "spring")
public interface User2Mapper {
    User2Mapper INSTANCE = Mappers.getMapper(User2Mapper.class);

    // 如果无特殊字段,可以不配置Mappings
    // 会自动把两个源对象中的属性复制到咪表对象
    @Mappings({
            @Mapping(source = "userDTO.name",target = "name"),
            @Mapping(source = "addressDTO.country",target = "country")
    })
    UserVO2 to(UserDTO userDTO, AddressDTO addressDTO);
}

测试

public class t3 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .build();

        AddressDTO addressDTO = AddressDTO.builder()
                .country("中国")
                .province("北京")
                .city("北京")
                .build();
        UserVO2 userVO2 = User2Mapper.INSTANCE.to(userDTO, addressDTO);
        System.out.println(userVO2);
    }
}

在这里插入图片描述

不同属性名之间的转换

两个对象间不同名称间属性值拷贝

测试对象

// UserDTO 略...

@Data
@ToString
public class UserVO3 {
    private String nickName;
}

Mapper

@Mapper(componentModel = "spring")
public interface User3Mapper extends BaseMapper<UserDTO,UserVO3>{
    User3Mapper INSTANCE = Mappers.getMapper(User3Mapper.class);

    @Mapping(source = "name", target = "nickName")
    @Override
    UserVO3 to(UserDTO var1);
}

测试

public class t3 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .build();

        UserVO3 userVO3 = User3Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO3);
    }
}
互相转换(反向转换)

如上示例,将的 UserDTO.name 转换为 UserVO3.nickName ;同时 UserVO3.nickName 也要能正常转换为 UserDTO.name,就可以通过@InheritInverseConfiguration 来实现

转换Mapper

@Mapper(componentModel = "spring")
public interface User3Mapper extends BaseMapper<UserDTO, UserVO3> {
    User3Mapper INSTANCE = Mappers.getMapper(User3Mapper.class);

    @Mapping(source = "name", target = "nickName")
    @Override
    UserVO3 to(UserDTO var1);

    // name为 A==>B 的方法名
    @InheritInverseConfiguration(name = "to")
    @Override
    UserDTO from(UserVO3 var1);
}

测试

/**
 * 不同属性名之间的映射
 */
public class t3 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .build();

        UserVO3 userVO3 = User3Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO3);

        UserDTO userDTO1 = User3Mapper.INSTANCE.from(userVO3);
        System.out.println(userDTO1);
    }
}
自定义格式转换

批量将一种类型的数据转换为另一种格式的数据;这里测试将所有的Date数据全部转换为 yyyy-MM-dd 的文本
测试对象

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserDTO {
    private String name;

    private Integer age;

    private Date createTime;

    private Date updateTime;
}
@Data
@ToString
public class UserVO1 {
    private String name;

    private Integer age;

    // 类型和VO对象不同
    private String createTime;

    // 类型和VO对象不同
    private String updateTime;
}

自定义日期格式转换Mapper

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

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

定义对象转换Mapper

/**
 * 自定义日期格式转换映射器
 * uses = DateMapper.class
 */
@Mapper(uses = DateMapper.class)
public interface User5Mapper extends BaseMapper<UserDTO, UserVO1> {
    User5Mapper INSTANCE = Mappers.getMapper(User5Mapper.class);
}

测试

public class t5 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .updateTime(new Date())
                .build();

        UserVO1 userVO1 = User5Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO1);
    }
}
多种不同自定义转换作用于不同属性

测试场景

对象中的 Date 字段转换成不同格式的时间文本,比如转换成 yyyy-MM-dd 和 yyyy/MM/dd 两种格式

测试对象

同上

自定义时间转换器

格式一

@Named("dateMapper1")
public class DateMapper1 {
    public String toString(Date date) {
        return date != null ? new SimpleDateFormat("yyyy-MM-dd").format(date) : null;
    }

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

测试二

@Named("dateMapper2")
public class DateMapper2 {
    public String toString(Date date) {
        return date != null ? new SimpleDateFormat("yyyy/MM/dd").format(date) : null;
    }

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

定义对象转换Mapper

/**
 * 自定义不同日期格式转换映射器
 * uses = DateMapper.class
 */
@Mapper(uses = {
        DateMapper1.class,
        DateMapper2.class
})
public interface User6Mapper extends BaseMapper<UserDTO, UserVO1> {
    User6Mapper INSTANCE = Mappers.getMapper(User6Mapper.class);

    @Mappings({
            @Mapping(source = "createTime", target = "createTime", qualifiedByName = {"dateMapper1"}),
            @Mapping(source = "updateTime", target = "updateTime", qualifiedByName = {"dateMapper2"})
    })
    @Override
    UserVO1 to(UserDTO var1);
}

测试

public class t6 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .updateTime(new Date())
                .build();

        UserVO1 userVO1 = User5Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO1);
    }
}
数字类型转换

场景

如果是基本的数据类型与文本之间的转换,默认情况下MapStruct 已经帮我们做好了,比如int与 string 的互转,就会自动通过String.valueof 以及 Integer.tostring 等方法进行转换了;

但是还存在一些特殊场景;比如高精度转换低精度,需要取小数点后多少位等,就需要特殊处理;

这里就来测试一个 double 转 string 保留两位小数的场景

测试对象

UserDTO添加以下字段

private Double wallet;

UserVO1添加以下字段

private String wallet;

定义对象转换Mapper

/**
 * 数值类型格式化
 */
@Mapper
public interface User7Mapper extends BaseMapper<UserDTO, UserVO1> {
    User7Mapper INSTANCE = Mappers.getMapper(User7Mapper.class);

    @Mapping(source = "wallet", target = "wallet", numberFormat = "$#.00")
    @Override
    UserVO1 to(UserDTO var1);

    @Mapping(source = "wallet", target = "wallet", numberFormat = "$#.00")
    @Override
    UserDTO from(UserVO1 var1);

    @IterableMapping(numberFormat = "$#.00")
    List<String> doubleList2String(List<Double> vas);

    @IterableMapping(numberFormat = "$#.00")
    List<Double> stringList2Double(List<String> vas);
}

测试

/**
 * 数值类型转换格式化
 */
public class t7 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .updateTime(new Date())
                .wallet(10000.45678)
                .build();

        UserVO1 userVO1 = User7Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO1);

        UserDTO userDTO1 = User7Mapper.INSTANCE.from(userVO1);
        System.out.println(userDTO1);

        List<Double> vas = new ArrayList<>();
        vas.add(123.5585);
        vas.add(784.1565488);
        vas.add(12.11243);
        // string list转 double
        List<String> strings = User7Mapper.INSTANCE.doubleList2String(vas);
        System.out.println(strings);

        // double list 转 string
        List<Double> doubles = User7Mapper.INSTANCE.stringList2Double(strings);
        System.out.println(doubles);
    }
}
BigDecimal转换

测试对象

UserDTO添加以下属性

private BigDecimal deposit;

UserVO1添加以下属性

private String deposit;

定义对象转换Mapper

/**
 * BigDecimal转换
 */
@Mapper
public interface User8Mapper extends BaseMapper<UserDTO, UserVO1> {
    User8Mapper INSTANCE = Mappers.getMapper(User8Mapper.class);

    @Mapping(source = "deposit", target = "deposit", numberFormat = "#.##E0")
    @Override
    UserVO1 to(UserDTO var1);

    @Mapping(source = "deposit", target = "deposit", numberFormat = "#.##E0")
    @Override
    UserDTO from(UserVO1 var1);

测试

/**
 * BigDecimal转换测试
 */
public class t8 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .createTime(new Date())
                .updateTime(new Date())
                .wallet(10000.45678)
                .deposit(new BigDecimal(10000000.324))
                .build();

        UserVO1 userVO1 = User8Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO1);

        UserDTO userDTO1 = User8Mapper.INSTANCE.from(userVO1);
        System.out.println(userDTO1);
    }
}
嵌套属性的转换

当对象中嵌套对象,且需要转换的时候,可以通过配置不同对象间的映射关系来完成嵌套映射

测试对象

UserDTO添加地址对象

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UserDTO {
    // 略...
    
    private AddressDTO addressDTO;
}

UserVO2

@Data
@ToString
public class UserVO2 {
    private String name;

    private Integer age;

    private String country;

    private String province;

    private String city;
}

测试需求

将UserDTO.addressDTO.country 属性映射到 UserVO2.country

映射Mapper

/**
 * 嵌套对象的映射
 */
@Mapper
public interface User9Mapper extends BaseMapper<UserDTO, UserVO2> {
    User9Mapper INSTANCE = Mappers.getMapper(User9Mapper.class);

    @Mapping(source = "addressDTO.country", target = "country")
    @Override
    UserVO2 to(UserDTO userDTO);

    // 反向配置
    @InheritInverseConfiguration(name = "to")
    @Override
    UserDTO from(UserVO2 var1);
}

测试代码

/**
 * 嵌套对象的映射
 */
public class t9 {
    public static void main(String[] args) {
        UserDTO userDTO = UserDTO.builder()
                .name("张三")
                .age(10)
                .addressDTO(AddressDTO.builder().country("中国").build())
                .build();

        UserVO2 userVO2 = User9Mapper.INSTANCE.to(userDTO);
        System.out.println(userVO2);

        UserDTO userDTO1 = User9Mapper.INSTANCE.from(userVO2);
        System.out.println(userDTO1);
    }
}

4BeanUtils与MapStruct性能对比

文章一开始就说到了 MapStruct 的性能要高于 BeanUtils ;经过了一轮使用之后,我们得来实测一下性能到底差多少?

测试场景

分别通过MapStruct 和 BeanUtils 将相同对象转换100W次,看看整体的耗时

测试代码

/**
 * BeanUtils与MapStruct性能对比
 */
public class t10 {
    public static void main(String[] args) {
        for (int j = 0; j < 10; j++) {
            Long start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                UserDTO userDTO = UserDTO.builder()
                        .name("张三")
                        .age(10)
                        .build();

                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(userDTO, userVO);
            }
            System.out.println("BeanUtils 100W次转换耗时:" + (System.currentTimeMillis() - start));

            start = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                UserDTO userDTO = UserDTO.builder()
                        .name("张三")
                        .age(10)
                        .build();

                UserVO1 userVO1 = User1Mapper.INSTANCE.to(userDTO);
            }
            System.out.println("MapStruct 100W次转换耗时:" + (System.currentTimeMillis() - start));
            System.out.println();
        }
    }
}

测试结果

可以看出,相同的属性转换,发现性能确实不在一个数量级;

在这里插入图片描述

5问题

问题一
找不到属性名

Error:(15, 5) java: No property named "xxx" exists in source parameter(s). Did you mean "null"?

lombok版本过高,将版本调低点

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.22</version>
</dependency>

问题二
修改无效

可以将target目录删除重新编译测试;防止因为修改为编译导致不生效的情况。

测试代码地址: https://github.com/mbb2100/mapstruct-demo

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 开发中,经常会遇到需要将一个类的属性转换为另一个类的属性的情况。这通常需要写很多重复的代码。为了减少这些重复的代码,可以使用 MapStruct 这个工具来处理恼人的 bean 转换。 MapStruct 是一个基于注解的 Java Bean 映射工具,它可以根据注解自动生成 Bean 映射代码。使用 MapStruct 可以让 bean 转换变得更加简单和高效。 接下来,我将介绍如何使用 MapStruct 处理 bean 转换。 1. 添加依赖 首先,需要在项目中添加 MapStruct 的依赖。可以在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.4.2.Final</version> </dependency> ``` 2. 创建转换接口 创建一个转换接口,用于定义源类和目标类之间的映射关系。例如,如果要将 User 类转换为 UserDto 类,可以创建以下接口: ```java @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); UserDto toUserDto(User user); } ``` 在接口上添加 @Mapper 注解,表示这是一个 MapStruct 映射接口。在接口中定义一个 toUserDto 方法,用于将 User 类转换为 UserDto 类。 3. 编写转换代码 在 toUserDto 方法中,使用 MapStruct 提供的映射注解来指定属性的映射关系。例如,以下代码将 User 类的 name 属性映射到 UserDto 类的 username 属性: ```java @Mapping(source = "name", target = "username") UserDto toUserDto(User user); ``` 4. 执行转换 创建一个 User 对象,并将其转换为 UserDto 对象: ```java User user = new User("Tom", 18); UserDto userDto = UserMapper.INSTANCE.toUserDto(user); ``` 使用 MapStruct 处理 bean 转换非常简单。只需定义一个转换接口,并在其中使用映射注解指定属性的映射关系,就可以自动生成 bean 映射代码。这样可以大大减少编写重复代码的工作量,提高开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值