热爱技术,追求卓越
不断求索,精益求精

【13】引入MapStruct,对象转化更轻松

从0开始搭建后台管理系统,这是No13。上一篇我们讲到了:Mybatis-Plus配置,实现多租户、逻辑删除、乐观锁。这一节,我们就来引入MapStruct。

MapStruct是一个 编译时的Java对象映射框架 ,通过注解处理器在编译阶段生成类型安全、高性能的映射代码,主要用于解决不同Java对象(如DTO与实体类、VO与BO等)之间的属性转换问题。

为什么引入MapStruct

  1. 高性能(编译时生成代码)
    MapStruct在 编译阶段 就生成完整的Java映射代码,避免了运行时反射(如ModelMapper、Dozer等框架的反射开销),执行效率接近手写的映射代码。
  2. 类型安全(编译时错误检查)
  • 映射错误在 编译阶段 即可发现(如字段名不匹配、类型不兼容等),避免运行时异常。
  • IDE会实时提示映射问题,支持代码补全和跳转。
    对比 :反射式框架(如ModelMapper)在运行时才会发现字段不匹配,增加调试难度。
  1. 配置灵活(注解驱动)
    提供丰富的注解支持复杂映射场景:
  • 字段名映射: @Mapping(source = “userId”, target = “id”)
  • 类型转换:内置基本类型(如String↔Date、Enum↔String)转换,支持自定义转换器。
  • 嵌套对象映射:自动处理嵌套对象的转换(如 User.address.city → UserDTO.city )。
  • 集合映射:支持List、Set、Map等集合类型的批量转换。
  1. 易于调试与维护
  • 生成的代码是标准Java代码,可直接阅读和调试(在 target/generated-sources 目录下)。
  • 映射逻辑集中在Mapper接口中,便于维护和重构。
  1. 低运行时依赖
    MapStruct核心依赖仅为 mapstruct (API)和 mapstruct-processor (编译时处理器),运行时无额外依赖,避免jar包冲突。

引入MapStruct

加入如下依赖

<dependency>
	<groupId>io.github.linpeilie</groupId>
	<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
	<version>${mapstruct.version}</version>
</dependency>

使用@AutoMapper自动转换视图对象

@Data
@AutoMapper(target = SysUser.class)
public class SysUserVo implements Serializable {

    
}

使用@AutoMapper自动转换业务对象

@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = SysUser.class, reverseConvertGenerate = false)
public class SysUserBo extends BaseEntity{
	
	@Serial
	private static final long serialVersionUID = 1L;

}

转换工具类

package cn.lovecto.yuen.common.utils;

import java.util.List;
import java.util.Map;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import io.github.linpeilie.Converter;

public class ConvertUtils {

    private final static Converter MAPSTRUCT_CONVERTER = SpringUtils.getBean(Converter.class);

    /**
     * 转换对象
     * 
     * @param source 源对象
     * @param desc   目标对象类型
     * @param <T>    源对象类型
     * @param <V>    目标对象类型
     * @return 目标对象
     */
    public static <T, V> V convert(T source, Class<V> desc) {
        if (ObjectUtil.isNull(source)) {
            return null;
        }
        if (ObjectUtil.isNull(desc)) {
            return null;
        }
        return MAPSTRUCT_CONVERTER.convert(source, desc);
    }

    /**
     * 转换对象
     * 
     * @param source 源对象
     * @param desc   目标对象
     * @param <T>    源对象类型
     * @param <V>    目标对象类型
     * @return 目标对象
     */
    public static <T, V> V convert(T source, V desc) {
        if (ObjectUtil.isNull(source)) {
            return null;
        }
        if (ObjectUtil.isNull(desc)) {
            return null;
        }
        return MAPSTRUCT_CONVERTER.convert(source, desc);
    }

    /**
     * 转换对象列表
     * 
     * @param sourceList 源对象列表
     * @param desc       目标对象类型
     * @param <T>        源对象类型
     * @param <V>        目标对象类型
     * @return 目标对象列表
     */
    public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
        if (ObjectUtil.isNull(sourceList)) {
            return null;
        }
        if (CollUtil.isEmpty(sourceList)) {
            return CollUtil.newArrayList();
        }
        return MAPSTRUCT_CONVERTER.convert(sourceList, desc);
    }

    /**
     * 转换Map对象
     * 
     * @param map       源Map对象
     * @param beanClass 目标对象类型
     * @param <T>       目标对象类型
     * @return 目标对象
     */
    public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
        if (MapUtil.isEmpty(map)) {
            return null;
        }
        if (ObjectUtil.isNull(beanClass)) {
            return null;
        }
        return MAPSTRUCT_CONVERTER.convert(map, beanClass);
    }

}

若出现类似报错:

io.github.linpeilie.ConvertException: cannot find converter from SysUser to SysUserVo

则在主项目的pom.xml中加入

<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<configuration>
				<source>${java.version}</source>
				<target>${java.version}</target>
				<encoding>${project.sourceEncoding}</encoding>
				<annotationProcessorPaths>
					<path>
						<groupId>com.github.therapi</groupId>
						<artifactId>therapi-runtime-javadoc-scribe</artifactId>
						<version>0.15.0</version>
					</path>
					<path>
						<groupId>org.projectlombok</groupId>
						<artifactId>lombok</artifactId>
						<version>${lombok.version}</version>
					</path>
					<path>
						<groupId>org.springframework.boot</groupId>
						<artifactId>spring-boot-configuration-processor</artifactId>
						<version>${spring.boot}</version>
					</path>
					<path>
						<groupId>io.github.linpeilie</groupId>
						<artifactId>mapstruct-plus-processor</artifactId>
						<version>${mapstruct.version}</version>
					</path>
					<path>
						<groupId>org.projectlombok</groupId>
						<artifactId>lombok-mapstruct-binding</artifactId>
						<version>${mapstruct.lombok.version}</version>
					</path>
				</annotationProcessorPaths>
				<compilerArgs>
					<arg>-parameters</arg>
				</compilerArgs>
			</configuration>
		</plugin>
	</plugins>
</build>

写个测试试试:

package cn.lovecto.yuen_app;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import cn.lovecto.yuen.YuenAppApplication;
import cn.lovecto.yuen.common.utils.ConvertUtils;
import cn.lovecto.yuen.system.domain.SysUser;
import cn.lovecto.yuen.system.domain.vo.SysUserVo;
import cn.lovecto.yuen.system.mapper.SysUserMapper;

@SpringBootTest(classes = YuenAppApplication.class)
public class ConvertUtilsTest {
	
	@Autowired
	private SysUserMapper userMapper;
	
	@Test
	public void convert() {
		try {
			LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
			wrapper.eq(SysUser::getUserName, "admin");
			List<SysUser> users = userMapper.selectList(wrapper);
			for(SysUser u : users) {
				SysUserVo vo = ConvertUtils.convert(u, SysUserVo.class);
				System.out.println(vo.getUserName());
			}
			List<SysUserVo> vos = ConvertUtils.convert(users, SysUserVo.class);
			for(SysUserVo vo : vos) {
				System.out.println(vo.getUserName());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
赞(0)
未经允许不得转载:LoveCTO » 【13】引入MapStruct,对象转化更轻松

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

热爱技术 追求卓越 精益求精