mapstruct原理以及使用对比
mapstruct原理以及使用对比
简单介绍:我们在java开发中,常常涉及到对象之间的转化,这时候常常有3种方式:
- 手动构建,通过set方法或者builder形式构建
- 使用BeanUtil.copyProperties()反射构建
- 使用mapstruct自动构建
第一种方式比较繁琐,当变量比较多的时候,我们手动一个一个构建很容易漏值,当然你可以使用一些插件,但是自己还是有比较重复的操作;
第二种方式底层原理是反射,效率比较低,虽然用的多,但是其实也不是很推荐;
第三种方式个人感觉是最好的方式,因为实际的实现就是系统自动的进行第一步操作:项目编译时会构建对应接口的实现类,然后将其中的方法都自动的通过builder的形式构建出来,然后我们通过自动构建的实现类就可以完成对象的转化了,具体如下:
简单示例
@Mapper(builder = @Builder(disableBuilder = true), uses = ObjectConvertUtil.class)
public interface LogObjectConverter {LogObjectConverter INSTANCE = Mappers.getMapper(LogObjectConverter.class);@Mapping(target = "createTime", source = "createTime", qualifiedByName = "dateFormat")@Mapping(target = "updateTime", source = "updateTime", qualifiedByName = "dateFormat")LogVO logPOToVO(Log log);Log logInsertDTOToPO(LogInsertDTO dto);Log logUpdateDTOToPO(LogUpdateDTO dto);}
@Mapper:这是 MapStruct 库的核心注解。它告诉 MapStruct 在编译时为此接口自动生成实现类。生成的代码会处理对象之间的属性拷贝。
builder = @Builder(disableBuilder = true):禁用生成器模式。
MapStruct 默认会为复杂映射生成一个建造者(Builder),但这里明确禁用了。通常是为了保持代码简洁,或者目标对象本身没有提供Builder。
uses = ObjectConvertUtil.class:声明要使用的自定义工具类。这是关键配置。它告诉 MapStruct:“当你在执行映射时,如果遇到难题(比如类型不匹配),可以去 ObjectConvertUtil 这个类里找我提供的方法来帮忙解决。”
LogObjectConverter INSTANCE = Mappers.getMapper(LogObjectConverter.class);
这一步是关键,获取 MapStruct 生成实现类的单例实例。
Mappers.getMapper():一个 MapStruct 提供的工具方法,用于获取自动生成的映射器实例。
这样,在其他代码中就可以通过 LogObjectConverter.INSTANCE.logPOToVO(log) 来轻松使用转换功能,而无需自己管理实例。
比如这个在编译后就会自动生成如下类:
@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2025-08-30T12:56:38+0800",comments = "version: 1.5.4.Final, compiler: javac, environment: Java 1.8.0_301 (Oracle Corporation)"
)
public class LogObjectConverterImpl implements LogObjectConverter {private final ObjectConvertUtil objectConvertUtil = new ObjectConvertUtil();@Overridepublic LogVO logPOToVO(Log log) {if ( log == null ) {return null;}LogVO logVO = new LogVO();logVO.setCreateTime( objectConvertUtil.dateFormat( log.getCreateTime() ) );logVO.setUpdateTime( objectConvertUtil.dateFormat( log.getUpdateTime() ) );logVO.setId( log.getId() );logVO.setOperatorType( log.getOperatorType() );logVO.setDescription( log.getDescription() );return logVO;}@Overridepublic Log logInsertDTOToPO(LogInsertDTO dto) {if ( dto == null ) {return null;}Log log = new Log();log.setOperatorType( dto.getOperatorType() );log.setDescription( dto.getDescription() );return log;}@Overridepublic Log logUpdateDTOToPO(LogUpdateDTO dto) {if ( dto == null ) {return null;}Log log = new Log();log.setId( dto.getId() );log.setOperatorType( dto.getOperatorType() );log.setDescription( dto.getDescription() );return log;}
}
下面将工具类给上,方便大家观看:
public class ObjectConvertUtil {/*** 将Date类型转成yyyy-MM-dd HH:mm:ss形式的String字符串* @param date 时间* @return string字符串*/@Named("dateFormat")public String dateFormat(Date date){if (date == null){return null;}return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);}
}
具体的使用如下:
LogObjectConverter.INSTANCE.logInsertDTOToPO()
后续在研究的时候发现有更有的方法:
优化
将mapstruct和spring进行融合,将其注入的spring容器中,这样我们就不用获取对应的实现类了:
@Mapper(componentModel = "spring", // 使用Spring的组件模型,生成的实现类会被注入Spring容器uses = CentralMappingConverter.class) // 使用通用的中央转换器
public interface LogObjectConverter1 {// @Mapping(target = "typeCode", source = "dataType.code")// 在反向映射时,source = "dataType.code" 这种点表达式,MapStruct 会自动调用枚举对象的 getCode() 方法,您不需要额外编写转换器。@Mapping(target = "createTime", source = "createTime", qualifiedByName = "formatDateTime")@Mapping(target = "updateTime", source = "updateTime", qualifiedByName = "formatDateTime")LogVO logPOToVO(Log log);Log logInsertDTOToPO(LogInsertDTO dto);Log logUpdateDTOToPO(LogUpdateDTO dto);}
对应的中央转化器:
@Component // 可以被 Spring 管理,方便注入(如果需要)
@Named("CentralMappingConverter") // 给它自己一个名字
public class CentralMappingConverter {private static final DateTimeFormatter DT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");/*** Date 转格式化的日期时间字符串 (yyyy-MM-dd HH:mm:ss)*/@Named("formatDateTime")public String formatDateTime(Date date) {if (date == null) {return null;}LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZONE_ID);return localDateTime.format(DT_FORMATTER);}
}
我们将对应的中央转换器注入到spring中,在具体的实现类中只需要将其LogObjectConverter1引入就可以直接使用了,对应生成的代码如下:
@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2025-08-30T12:56:38+0800",comments = "version: 1.5.4.Final, compiler: javac, environment: Java 1.8.0_301 (Oracle Corporation)"
)
@Component
public class LogObjectConverter1Impl implements LogObjectConverter1 {@Autowiredprivate CentralMappingConverter centralMappingConverter;@Overridepublic LogVO logPOToVO(Log log) {if (log == null) {return null;}LogVO.LogVOBuilder<?, ?> logVO = LogVO.builder();logVO.createTime(centralMappingConverter.formatDateTime(log.getCreateTime()));logVO.updateTime(centralMappingConverter.formatDateTime(log.getUpdateTime()));logVO.id(log.getId());logVO.operatorType(log.getOperatorType());logVO.description(log.getDescription());// 123return logVO.build();}@Overridepublic Log logInsertDTOToPO(LogInsertDTO dto) {if (dto == null) {return null;}Log.LogBuilder<?, ?> log = Log.builder();log.operatorType(dto.getOperatorType());log.description(dto.getDescription());return log.build();}@Overridepublic Log logUpdateDTOToPO(LogUpdateDTO dto) {if (dto == null) {return null;}Log.LogBuilder<?, ?> log = Log.builder();log.id(dto.getId());log.operatorType(dto.getOperatorType());log.description(dto.getDescription());return log.build();}
}