MapStruct

424 阅读4分钟

MapStruct入门

官方文档:mapstruct.org/documentati…

介绍

​ MapStruct是一个开源的java对象映射框架,它用于简化不同类型对象之间的转换过程。提供了一种自动生成对象映射代码的机制,可以大大减少手动编写转换代码的工作量,提高开发效率

MapStruct 的优点包括:

  • 类型安全:MapStruct 在编译时会检查类型,并提供编译期错误提示,避免运行时错误。
  • 高性能:MapStruct 生成的转换代码经过优化,执行效率较高。
  • 可配置性强:可以通过注解和自定义映射器来处理复杂的转换逻辑。
  • 易于集成:MapStruct 可与常见的构建工具和框架(如 Maven、Gradle、Spring)无缝集成。

原理

MapStruct 是一个 Java 注解处理器,它基于注解和代码生成技术来实现对象之间的映射。其原理可以分为以下几个步骤:

  1. 定义映射接口:创建一个使用 @Mapper 注解标记的接口,该接口定义了源对象到目标对象的映射方法。
  2. 编译时代码生成:在编译时,MapStruct 注解处理器会扫描项目中的映射接口,并根据接口的定义信息进行代码生成。生成的代码包括具体的映射方法的实现,以及必要的辅助类和方法。
  3. 映射方法实现:生成的映射方法会根据源对象和目标对象的属性名称和类型,自动进行属性值的拷贝。在映射过程中,MapStruct 会根据类型转换规则进行类型的自动转换,例如基本类型到包装类型的转换、日期类型到字符串的转换等。
  4. 自定义映射配置:如果需要进行更复杂的转换逻辑,可以在映射接口中定义自定义的映射方法,并在方法中编写转换逻辑。这些自定义方法可以通过 @Mapping 注解来指定源属性和目标属性之间的映射关系。
  5. 使用映射接口:在应用程序中,可以通过获取映射接口的实例,然后调用其中的映射方法来完成对象之间的转换操作。

通过以上的步骤,MapStruct 实现了简化对象之间映射的过程。它可以帮助开发人员自动生成类型安全的映射代码,减少手动编写转换逻辑的工作量,并提高代码的可维护性和可读性。

如何使用MapStruct

1、引入依赖

maven:

<!-- MapStruct核心包 -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<!-- 作用是编译时根据MapStruct的注解生成实现类-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>

2、具体使用

属性一致类相互转换

创建转换接口,定义转换方法

package com.yxx.mapstruct.mapstruct;

import com.yxx.mapstruct.dto.UserDto;
import com.yxx.mapstruct.entity.User;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring")
public interface UserMapStruct{
    // 可以不使用componentModel = "spring"
    UserMapStruct INSTANCE = Mappers.getMapper(UserMapStruct.class);

    UserDto toUserDto(User user);

    List<UserDto> toUserDtoList(List<User> userList);
}

此接口在项目编译后,会生成一个实现类,见下发

componentModel = "spring",编译后会自动变成@Component注解,方便其他类注入

编译后生成的接口实现类:UserMapStructImpl

package com.yxx.mapstruct.mapstruct;

import com.yxx.mapstruct.dto.UserDto;
import com.yxx.mapstruct.entity.User;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;


@Component
public class UserMapStructImpl
  implements UserMapStruct
{
  public UserDto toUserDto(User user) {
    if (user == null) {
      return null;
    }
    
    UserDto userDto = new UserDto();
    
    userDto.setName(user.getName());
    userDto.setSex(user.getSex());
    userDto.setRemark(user.getRemark());
    userDto.setPassword(user.getPassword());
    
    return userDto;
  }

  
  public List<UserDto> toUserDtoList(List<User> userList) {
    if (userList == null) {
      return null;
    }
    
    List<UserDto> list = new ArrayList<>(userList.size());
    for (User user : userList) {
      list.add(toUserDto(user));
    }
    
    return list;
  }
}

需要忽略属性转换

方法1,目标对象userDto删除相关属性,重新编译

方法2:使用@Mapping(target = "password", ignore = true)忽略目标对象属性

@Mapping(target = "password", ignore = true) // 忽略password转换
UserDto toUserDto(User user);

不同属性名称转换

@Mapping(source = "userName",target = "name") // 名称不同转换 

转换的类包含引用类不同转换

//1.指定来源属性和目标属性
@Mapping(target = "schoolDto", source = "school") // 引用类型属性转换
UserDto toUserDto(User user);

//2.添加引用类型的转换方法
SchoolDto toSchoolDto(School school);

日期类型转换

@Mapping(target = "updateTime", source = "updateTime", dateFormat = "yyyy-MM-dd") //日期格式转换

// Date -> String,生成的实现类
if (user.getUpdateTime() != null) {
  userDto.setUpdateTime((new SimpleDateFormat("yyyy-MM-dd")).format(user.getUpdateTime()));
}

// String -> Date,生成的实现类
try {
  if (user.getUpdateTime() != null) {
    userDto.setUpdateTime((new SimpleDateFormat("yyyy-MM-dd")).parse(user.getUpdateTime()));
  }
}
catch (ParseException e) {
  throw new RuntimeException(e);
} 

固定值转换

// 无论源属性值是什么,目标属性值都是固定的
@Mapping(target = "name", constant = "liudehua")

自定义表达式转换

// 1.增加expression="java(方法名(方法参数))"格式的表达式,注意source和expression不能同时使用,编译会报错
@Mapping(target = "gender",expression = "java(sexToGender(user))")
UserDto toUserDto(User user);

// 2.增加对应名称参数的转换方法
default String sexToGender(User user) {
    if (user.getSex() == 1) {
        return "男";
    }
    return "女";
}

其他转换

基本类型和对应的包装类型中间是自动转换的,原理通过Integer.valueOf 或者 intValue()转换

基本类型都能转换为String,原理通过String.valueOf转换