MapStruct是什么?
MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.
The generated mapping code uses plain method invocations and thus is fast, type-safe and easy to understand.
MapStruct是一个基于Java注解的代码生成器,它用于在编译时生成Java Bean之间的映射代码。它的实现原理基于JSR 269,这是JDK引进的一种规范,允许在编译期处理注解,并且读取、修改和添加抽象语法树中的内容。
在定义了一个包含MapStruct注解的映射器接口后,MapStruct的注解处理器会在编译时扫描这些接口,并生成对应的实现类。这些实现类包含了将源对象映射到目标对象的具体代码,它们使用纯Java的方法调用,而非反射机制,因此具有高性能和类型安全的特性。
MapStruct的主要特点:
- 类型安全:通过编译时生成代码,确保了源对象和目标对象之间的映射是类型安全的。这有助于在编写代码时更容易地检测到错误,并减少运行时错误的风险。
- 高性能:由于MapStruct生成的代码是在编译时生成的,并且直接调用这些实现类的方法,因此在运行时执行对象映射时速度非常快,不需要进行反射调用。
- 支持多种映射策略:MapStruct支持多种映射策略,包括注释、名称约定和自定义映射方法等,以满足不同的转换需求。
- 易于集成:MapStruct可以无缝集成到Spring等IoC容器中,方便地将MapStruct转换器注入到应用程序中。
MapStruct适用场景:
MapStruct适用于各种Java Bean之间的转换场景,包括但不限于以下情况:
- DTO(Data Transfer Object)和Entity之间的转换。
- 不同版本API之间的数据映射。
- 前端页面展示数据和后端数据模型之间的转换。
在这些场景中,MapStruct可以帮助开发者简化转换代码的编写,提高开发效率,减少出错的可能性。
代码示例:(jdk8+maven+Intellj Idea 2021.3.1)
pom.xml加入依赖
<!-- MapStruct core dependency --><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${mapstruct.version}</version></dependency><!-- MapStruct annotation processor (needed for compile-time code generation) --><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${mapstruct.version}</version><scope>provided</scope></dependency>
编写源对象和目标对象:
public class Car { private String make; private String model; // getters and setters
} public class CarDto { private String make; private String model; // getters and setters
}
定义映射接口:
@Mapper
public interface CarMapper { CarDto carToCarDto(Car car); Car carToCar(CarDto carDto); // 其他映射方法...
}
自定义映射:
在某些情况下,你可能需要进行一些自定义映射操作。MapStruct提供了多种方式来支持自定义映射。
-
使用@Mapping注解:你可以使用
@Mapping
注解来指定源属性和目标属性之间的映射关系。
@Mapper
public interface CarMapper { @Mapping(source = "carId", target = "id") CarDto carToCarDto(Car car); // ...
}
- 自定义类型转换:你可以通过编写自定义的转换方法或使用MapStruct提供的类型转换注解(如
@NumberFormat
、@DateTimeFormat
等)来实现复杂的类型转换。 - 使用@Qualifier:在存在多个转换方法的情况下,你可以使用
@Qualifier
注解来指定使用哪个方法。 - 后处理器:在某些情况下,你可能需要在映射完成后进行一些操作。这时,你可以使用MapStruct的后处理器功能。
使用映射器:
一旦你定义了映射接口并配置了必要的映射方法,MapStruct将在编译时自动生成这个接口的实现类。你可以通过Mappers.getMapper()
方法或其他依赖注入机制来获取映射器实例,并使用它来执行映射操作。
CarMapper carMapper = Mappers.getMapper(CarMapper.class);
CarDto carDto = carMapper.carToCarDto(car);
在这个例子中,我们通过Mappers.getMapper()
方法获取了CarMapper
的实例,并使用它将一个Car
对象转换为一个CarDto
对象。
注意事项:
- 确保你的IDE或构建工具支持注解处理器。这通常需要在IDE的设置中启用注解处理器,或者在构建工具的配置文件中添加相应的插件或配置。
- 如果你在使用Lombok,并且遇到了与MapStruct的兼容性问题,请确保Lombok的版本与MapStruct的版本兼容。
- 在编写映射方法时,尽量使用简单的getter/setter方法调用,避免在映射方法中进行复杂的业务逻辑操作。这样可以提高映射的性能和可维护性。
BeanUtils:(以commons-beanutils为例)
BeanUtils是Apache Commons项目的一部分,它提供了一组用于复制Java Bean属性的实用程序方法。
MapStruct与BeanUtils处理对象属性复制场景对比:
MapStruct与BeanUtils在处理对象属性复制的场景中存在明显的差异,下面将从多个方面对它们进行对比:
- 技术原理:
- MapStruct:基于JSR 269的Java注释处理器,用于生成类型安全的Bean映射类。在编译过程中,MapStruct会生成映射器接口的实现,这些实现使用纯Java方法调用在源对象和目标对象之间进行映射,无需反射或类似操作。
- BeanUtils:在Spring框架中,BeanUtils.copyProperties方法用于将一个Java对象的属性值复制到另一个对象中。其实现机制通常基于反射。
- 性能:
- MapStruct:由于使用纯Java方法调用,MapStruct通常比基于反射的工具(如BeanUtils)具有更好的性能。特别是当处理大量对象拷贝时,MapStruct的性能优势更加明显。
- BeanUtils:基于反射的实现可能会导致性能下降,尤其是在处理大量对象时。
- 类型安全性:
- MapStruct:编译时类型安全,只能映射彼此映射的对象和属性,避免了意外的类型错误。
- BeanUtils:运行时类型安全,如果在运行时源对象和目标对象的属性类型不匹配,可能会抛出异常。
- 配置和灵活性:
- MapStruct:支持复杂的映射关系,如嵌套对象、集合、自定义映射方法等。通过注解可以灵活配置映射规则。
- BeanUtils:支持基本的属性复制,但对于复杂的映射关系可能需要额外的代码来处理。
- 错误报告:
- MapStruct:在构建时清除错误报告,如果映射不完整或映射不正确,可以在编译时发现问题。
- BeanUtils:在运行时发现问题,可能会抛出异常。
- 集成与依赖:
- MapStruct:需要额外的依赖项,但通常不会对现有项目产生太大影响。可以与多种Java项目集成。
- BeanUtils:如果项目已经集成了Spring框架,那么BeanUtils是现成的选择,无需额外依赖。
- 使用场景:
- MapStruct:适用于对性能有较高要求、需要处理大量对象拷贝、支持复杂映射关系的场景。
- BeanUtils:适用于简单的属性复制场景,尤其是在已经集成了Spring框架的项目中。
综上所述,MapStruct和BeanUtils各有优缺点,适用于不同的场景。在选择使用哪个工具时,需要根据具体业务场景和需求进行权衡取舍。如果需要处理大量对象拷贝、支持复杂映射关系且对性能有较高要求,建议使用MapStruct;如果项目已经集成了Spring框架且只需要简单的属性复制,那么BeanUtils是一个不错的选择。
mapstruct官网传送门:
MapStruct – Java bean mappings, the easy way!