MapStruct
MapStruct是一个用于Java的代码生成库,它基于注解处理器在编译时生成类型安全且高性能的对象映射代码。MapStruct可以帮助开发者减少手动编写繁琐的JavaBean之间的转换代码,提高代码的可读性和维护性。
什么是 MapStruct
MapStruct 核心概念
MapStruct是一个Java注解处理器,它的主要功能是自动生成类型安全、高性能且无依赖的bean映射代码。这个工具基于“约定优于配置”的原则,极大地简化了Java Bean类型之间的映射实现过程。
在多层架构的应用中,经常需要在不同的对象模型之间进行转换,例如在持久层的实体和传输层的DTO(Data Transfer Object,数据传输对象)之间。手动编写这种映射代码是一项繁琐且容易出错的任务。MapStruct通过自动化的方式解决了这个问题,它可以在编译时生成映射代码,从而保证了高性能、快速的开发反馈以及严格的错误检查。
具体来说,使用MapStruct时,开发者只需要定义一个接口,并在接口中定义转换方法。然后,MapStruct会自动生成实现这些方法的代码。这些生成的代码使用纯方法调用,因此速度快、类型安全且易于理解。
MapStruc主要特性
1、类型安全:MapStruct在编译时生成映射代码并进行类型检查,如果源对象和目标对象的属性不匹配,会在编译阶段就报错。
2、性能优秀:由于MapStruct在编译时就生成了映射代码,运行时无需通过反射进行属性拷贝,因此性能较高。
3、灵活性:MapStruct支持复杂的映射,如嵌套映射、集合映射、自定义转换规则等。
4、简洁性:MapStruct使用注解来定义映射规则,使得映射规则的定义更加直观和简洁。
5、无依赖:MapStruct不依赖于任何第三方库,可以很容易地集成到任何项目中。
6、集成Spring:MapStruct也可以与Spring框架集成,允许在映射器中注入Spring管理的bean。
使用MapStruct,开发者只需要定义一个接口,并在接口中声明源对象和目标对象之间的映射关系,MapStruct会在编译时自动生成映射实现类。这极大地提高了代码的可读性和可维护性,同时也避免了手动编写繁琐的转换代码。
MapStruct和BeanUtils区别
MapStruct和BeanUtils都是Java中常用的对象属性映射工具,但它们在使用方式和性能上有一些区别。
1、使用方式:
BeanUtils:使用反射机制进行属性拷贝,使用简单,无需写额外的映射代码。
MapStruct:需要定义映射接口,在编译阶段生成映射实现类,使用注解来定义源对象和目标对象之间的映射关系。
2、性能:
BeanUtils:由于使用了反射机制,性能较低。
MapStruct:在编译阶段就生成了映射代码,运行时无需通过反射进行属性拷贝,因此性能较高。
3、灵活性和安全性:
BeanUtils:由于是动态映射,如果源对象和目标对象的属性不匹配,可能会在运行时出现错误。
MapStruct:在编译阶段就进行了类型检查,如果源对象和目标对象的属性不匹配,会在编译阶段就报错,提高了类型安全性。另外,也支持复杂的映射,如嵌套映射、集合映射等。
入门示例
添加依赖
pom.xml
<dependencies>
<!-- Spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mapstruct -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.0</version>
</dependency>
<!-- Mapstruct processor -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.0</version>
<scope>provided</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
解决MapStruct与Lombok的冲突问题
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<!-- Lombok 在编译时插件 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
<!-- This is needed when using Lombok 1.18.16 and above -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</dependency>
<!-- Mapstruct should follow the lombok path(s) -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
定义类
Person.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person{
private String name;
}
PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String name;
}
定义Mapper接口
定义一个Mapper接口,这个接口将包含你想要转换的方法
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
PersonDTO personToPersonDto(Person person);
}
测试
public class PersonMapperTest {
@Test
public void test1() {
Person person = new Person("张三");
PersonDTO personDTO = PersonMapper.INSTANCE.personToPersonDto( person );
System.out.println( personDTO);
}
}
输出打印的内容如下
PersonDTO(name=张三)
自定义映射
默认映射是两者属性名一致时,映射到两边对应的属性中,当属性名不一致时,就需要自定义映射了
修改po类
Person.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person{
private String name;
}
PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fuleName;
}
自定义映射属性
如果还使用之前的映射,会报如下错误
java.lang.NoSuchMethodError: 'void net.wanho.PersonDTO.setName(java.lang.String)'
使用自定义映射进行配置
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
@Mapping(source = "name", target = "fullName")
PersonDto personToPersonDto(Person person);
}
使用Mapper
修改之后就可以在你的代码中使用它了,跟之前使用方式一致
public class MapTest {
@Test
public void test1() {
Person person = new Person("张三");
PersonDTO personDTO = PersonMapper.INSTANCE.personToPersonDto( person );
System.out.println( personDTO);
}
}
其它属性
在看完source和target属性的使用,我们再来看还哪哪些其它属性,这里先说一下:constant、expression和qualifiedByName的使用场景和使用方法。
costant
@Mapping注解constant属性可以用于将源对象的某个固定值映射到目标对象的属性。
- PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fullName;
private Integer age;
}
- PersonMapper.java
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
@Mapping(source = "name",target = "fullName")
@Mapping(target = "age",constant = "18")
PersonDTO personToPersonDto(Person person);
}
expression
@Mapping注解expression属性允许你使用Java表达式来定义字段映射。这在源和目标字段之间需要一些特定逻辑时非常有用。
- PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fullName;
private Integer age;
private Date birth;
}
- PersonMapper.java
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
@Mapping(source = "name",target = "fullName")
@Mapping(target = "age",constant = "18")
@Mapping(target = "birth",expression = "java(new java.util.Date())")
PersonDTO personToPersonDto(Person person);
}
qualifiedByName
@Mapping注解qualifiedByName属性允许你引用一个具有@Named注解的方法作为自定义的映射逻辑。
- PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fullName;
private Integer age;
private Date birth;
private String customerName;
}
- PersonMapper.java
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
@Mapping(source = "name",target = "fullName")
@Mapping(target = "age",constant = "18")
@Mapping(target = "birth",expression = "java(new java.util.Date())")
@Mapping(source = "person", target = "customerName",qualifiedByName = "defineCustomerName")
PersonDTO personToPersonDto(Person person);
@Named("defineCustomerName")
default String defineCustomerName(Person person) {
return "Customer Name - "+ person.getName();
}
}
@Mappers注解
@Mappers注解可以定义一组@Mapper映射
- PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fullName;
private Integer age;
private Date birth;
private String customerName;
}
- PersonMapper.java
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mappings({
@Mapping(source = "name", target = "fullName"),
@Mapping(target = "age", constant = "18"),
@Mapping(target = "birth", expression = "java(new java.util.Date())"),
@Mapping(source = "person", target = "customerName", qualifiedByName = "defineCustomerName")
})
PersonDTO personToPersonDto(Person person);
@Named("defineCustomerName")
default String defineCustomerName(Person person) {
return "Customer Name - " + person.getName();
}
}
@BeanMapping注解
从MapStruct 1.5开始,可以使用@BeanMapping注解在MapStruct中用于在映射方法级别提供更详细的配置。这个注解有许多参数可以使用,例如,你可以选择在更新时忽略null值
以下是一些常见的使用场景:
resultType
这个参数允许你指定映射方法的返回类型。这在目标类型可以是多个实现类时非常有用。
如果目标类型有多个实现类,并且你希望在映射时使用特定的实现类。通过指定resultType,你可以确保生成的映射代码使用正确的目标类型
@BeanMapping(resultType = PersonDTO.class)
PersonDTO personToPersonDto(Person person);
ignoreByDefault
ignoreByDefault: 这个参数允许你忽略所有未明确映射的属性。然后,你可以使用@Mapping注解来明确需要映射的属性。
@BeanMapping(ignoreByDefault = true)
@Mapping(target = "name", source = "fullName")
PersonDTO personToPersonDto(Person person);
nullValuePropertyMappingStrategy
nullValuePropertyMappingStrategy: 这个参数允许你指定当源属性为null时应如何处理目标属性。例如,你可以选择是否在源属性为null时调用目标的setter方法。
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
PersonDTO personToPersonDto(Person person);
其它映射
集合映射
普通方式
MapStruct也支持集合的映射,你可以很方便地将一个对象的集合转换为另一个对象的集合。
PersonMapper.java
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mappings({
@Mapping(source = "name", target = "fullName"),
@Mapping(target = "age", constant = "18"),
@Mapping(target = "birth", expression = "java(new java.util.Date())"),
@Mapping(source = "person", target = "customerName", qualifiedByName = "defineCustomerName")
})
@BeanMapping(resultType = PersonDTO.class)
PersonDTO personToPersonDto(Person person);
@Named("defineCustomerName")
default String defineCustomerName(Person person) {
return "Customer Name - " + person.getName();
}
@Mappings({
@Mapping(source = "name", target = "fullName"),
@Mapping(target = "age", constant = "18"),
@Mapping(target = "birth", expression = "java(new java.util.Date())"),
@Mapping(source = "person", target = "customerName", qualifiedByName = "defineCustomerName")
})
@BeanMapping(resultType = PersonDTO.class)
List<PersonDTO> personListToPersonDTOList(List<Person> personList);
}
PersonMapperTest.java
public class PersonMapperTest {
@Test
public void test1() {
Person person = new Person("张三");
PersonDTO personDTO = PersonMapper.INSTANCE.personToPersonDto( person );
System.out.println( personDTO);
}
@Test
public void test2() {
List<Person> personList = Arrays.asList(new Person("张三"), new Person("李四"), new Person("王五"));
List<PersonDTO> personDTOList = PersonMapper.INSTANCE.personListToPersonDTOList( personList );
System.out.println( personDTOList );
}
}
使用@IterableMapping和@MapMapping处理集合
当处理集合和映射时,你可能需要特定的转换规则。@IterableMapping 注解的作用是定义一个方法,用于将一个 Iterable 类型的源对象集合映射为目标对象集合。
具体来说,@IterableMapping 注解用于标记一个接口方法,该方法的参数类型为源对象集合,返回类型为目标对象集合。在生成的映射代码中,MapStruct 会将每个源对象映射为一个目标对象,并将它们添加到目标对象集合中。需要注意的是,源对象集合和目标对象集合的元素类型可以不同,此时需要手动指定元素类型转换方式。
@IterableMapping 注解还有一些属性,用于配置映射的行为,
例如:
qualifiedBy:用于指定一个限定符注解,当存在多个映射器时,可以使用该属性来选择特定的映射器。
elementTargetType:用于指定目标对象集合的元素类型。
nullValueMappingStrategy:用于处理源对象集合中包含空对象或者 null 值的情况。
一个示例的 @IterableMapping 注解的使用方式如下所示:
@Mapper
public interface UserMapper {
@IterableMapping(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
List<UserDTO> toUserDTOList(Iterable<User> users);
}
上述代码中,UserMapper 接口中的 toUserDTOList 方法使用了 @IterableMapping 注解,用于将 User 集合转换为 UserDTO 集合。其中,nullValueMappingStrategy 属性指定当源对象集合中包含空对象或者 null 值时,返回默认值。
使用@MapMapping 注解来处理 Map 类型的映射
@MapMapping 注解用于方法级别,指示 MapStruct 如何映射 Map 类型的属性。你可以在映射器接口中的方法上使用该注解,并提供一些配置选项。
@Mapper
public interface PersonMapper {
@MapMapping(keyTargetType = String.class, valueTargetType = PersonDto.class)
Map<String, PersonDto> personsToPersonDtos(Map<String, Person> persons);
}
在这个示例中,我们定义了一个名为 PersonMapper 的映射器接口,并使用了 @Mapper 注解将它标记为 MapStruct 映射器。
然后,我们在 personsToPersonDtos 方法上使用了 @MapMapping 注解,并提供了以下配置选项:
keyTargetType = String.class:指定目标键类型为 String。这会告诉 MapStruct 将源 Map 的键映射为 String 类型。
valueTargetType = PersonDto.class:指定目标值类型为 PersonDto。这会告诉 MapStruct 将源 Map 的值映射为 PersonDto 类型。
通过这样配置 @MapMapping 注解,MapStruct 将自动生成适当的映射代码,按照指定的映射规则将源 Map 中的键值对映射到目标 Map。
需要注意的是,如果你的映射逻辑更加复杂,可以在 @MapMapping 注解的方法参数中提供自定义的转换器。例如:
@Mapper
public interface PersonMapper {
@MapMapping(keyTargetType = String.class, valueTargetType = PersonDto.class,
keyQualifiedBy = {ToUpperCase.class}, valueQualifiedBy = {ConvertValue.class})
Map<String, PersonDto> personsToPersonDtos(Map<String, Person> persons);
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ToUpperCase {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ConvertValue {
Class<?> targetType();
}
@ToUpperCase
public String toUpperCase(String key) {
return key.toUpperCase();
}
@ConvertValue(targetType = PersonDto.class)
public PersonDto convertValue(Person person) {
// Custom conversion logic
return new PersonDto(person.getMake(), person.getNumberOfSeats());
}
}
在这个示例中,我们添加了自定义的转换器。通过使用 keyQualifiedBy 和 valueQualifiedBy 参数,我们可以指定用于键和值的转换器。
我们定义了两个自定义的限定符注解 @ToUpperCase 和 @ConvertValue,并在转换器方法上使用它们。然后,在 personsToPersonDtos 方法上分别指定了这两个限定符注解。
这样,当 MapStruct 遇到需要转换键或值的情况时,它将使用相应的转换器方法来进行转换。
枚举映射
MapStruct 的 @ValueMapping 注解是用来映射枚举值的。这个注解只能在 @Mapper 的接口或抽象类中使用。
下面是一个简单的例子,展示了如何使用 @ValueMapping 在两个枚举类型之间进行映射:
首先,我们定义两个枚举类型:
Status.java
public enum Status {
ACTIVE,
INACTIVE,
PENDING,
DELETED
}
StatusDTO.java
public enum StatusDTO {
ACTIVE,
INACTIVE,
WAITING,
REMOVED
}
然后,修改Person和PersonDTO类:
Person.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person{
private String name;
private Status status;
}
PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fullName;
private Integer age;
private Date birth;
private String customerName;
private StatusDTO statusDTO;
}
最后再修改一下PersonMapper类
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
// 自定义枚举映射,将 Status 映射为 StatusDTO
@ValueMapping(source = "PENDING", target = "WAITING")
@ValueMapping(source = "DELETED", target = "REMOVED")
StatusDTO mapStatus(Status status);
@Mappings({
@Mapping(source = "name", target = "fullName"),
@Mapping(target = "age", constant = "18"),
@Mapping(target = "birth", expression = "java(new java.util.Date())"),
@Mapping(source = "person", target = "customerName", qualifiedByName = "defineCustomerName")
})
@BeanMapping(resultType = PersonDTO.class)
@Mapping(source = "status", target = "statusDTO")
PersonDTO personToPersonDto(Person person);
}
测试代码跟之前一样
PersonMapperTest.java
public class PersonMapperTest {
@Test
public void test1() {
Person person = new Person("张三",Status.PENDING);
PersonDTO personDTO = PersonMapper.INSTANCE.personToPersonDto( person );
System.out.println( personDTO);
}
}
构造方法映射
从MapStruct 1.5开始,你可以使用构造方法来创建目标对象。你只需要在你的目标类中定义一个合适的构造方法,MapStruct就会自动使用它。
public class PersonDTO {
private final String make;
private final int seatCount;
public PersonDTO(String make, int seatCount) {
this.make = make;
this.seatCount = seatCount;
}
// getters
}
然后在你的Mapper接口中定义映射方法:
@Mapper
public interface PersonMapper {
PersonDTO personToPersonDto(Person person);
}
MapStruct将使用PersonDTO的构造方法来创建新的PersonDTO实例。
嵌套属性映射
MapStruct也支持嵌套属性的映射。例如,如果你的Person类有一个Engine对象,对象中有horsePower,你可以这样定义你的Mapper:
@Mapper
public interface PersonMapper {
@Mapping(source = "engine.horsePower", target = "horsePower")
PersonDTO personToPersonDto(Person person);
}
在这个例子中,personToPersonDto方法将Person的engine.horsePower属性映射到PersonDTO的horsePower属性。
反向映射
MapStruct还提供了反向映射的功能。你可以使用@InheritInverseConfiguration注解来创建反向的映射方法:
@Mapper
public interface PersonMapper {
PersonDTO personToPersonDto(Person person);
@InheritInverseConfiguration
Person personDtoToPerson(PersonDTO personDTO);
}
在这个例子中,personDTOToPerson方法是personDtoToPerson方法的反向映射。
使用装饰器增强Mapper
你可以使用装饰器来增强你的Mapper。首先,定义一个装饰器类:
public abstract class PersonMapperDecorator implements PersonMapper {
private final PersonMapper delegate;
public PersonMapperDecorator(PersonMapper delegate) {
this.delegate = delegate;
}
@Override
public PersonDTO personToPersonDto(Person person) {
PersonDTO dto = delegate.personToPersonDto(person);
dto.setMake(dto.getMake().toUpperCase());
return dto;
}
}
然后在你的Mapper接口中使用@DecoratedWith注解:
@Mapper
@DecoratedWith(PersonMapperDecorator.class)
public interface PersonMapper {
PersonDTO personToPersonDto(Person person);
}
映射继承
可以使用@InheritConfiguration注解使一个映射方法继承另一个映射方法的配置。例如:
@Mapper
public interface MyMapper {
@Mapping(target = "name", source = "name")
@Mapping(target = "description", source = "description")
Target sourceToTarget(Source source);
@InheritConfiguration
Target updateTargetFromSource(Source source, @MappingTarget Target target);
}
在上述代码中,我们首先定义了一个 sourceToTarget 方法,它将 Source 对象映射到 Target 对象。然后,我们定义了一个 updateTargetFromSource 方法,它接受一个 Source 对象和一个 Target 对象,并使用 @InheritConfiguration 注解来指示 MapStruct 使用 sourceToTarget 方法的映射配置。这意味着 updateTargetFromSource 方法将使用与 sourceToTarget 相同的映射规则。
注意:@InheritConfiguration 会寻找签名(参数数量和类型)最匹配的方法来继承配置,如果有多个匹配的方法,你需要使用 @InheritConfiguration(name=“…”) 来明确指定要继承的方法。
使用@BeforeMapping和@AfterMapping进行预处理和后处理
你可以使用@BeforeMapping和@AfterMapping注解来进行映射前后的处理:
@Mapper
public abstract class PersonMapper {
@BeforeMapping
protected void enrichPerson(Person person) {
person.setMake(person.getMake().toUpperCase());
}
@Mapping(source = "numberOfSeats", target = "seatCount")
public abstract PersonDto personToPersonDto(Person person);
@AfterMapping
protected void enrichDto(Person person, @MappingTarget PersonDto personDto) {
personDto.setSeatCount(person.getNumberOfSeats());
}
}
在这个例子中,enrichPerson方法在映射前被调用,enrichDto方法在映射后被调用。
使用@Context传递上下文参数
你可以使用@Context注解来传递上下文参数给映射方法:
@Mapper
public interface OrderMapper {
@Mapping(target = "customer", source = "entity.customer", qualifiedByName = "fullName")
OrderDto orderToOrderDto(Order entity, @Context CycleAvoidingMappingContext context);
@Named("fullName")
default String customerToString(Customer customer) {
return customer.getFirstName() + " " + customer.getLastName();
}
}
在这个例子中,context参数被用于避免循环引用。
映射更新
MapStruct允许你将一个对象的属性更新到另一个已存在的对象。例如:
@Mapper
public interface PersonMapper {
@Mapping(source = "numberOfSeats", target = "seatCount")
void updatePersonFromDto(PersonDto personDto, @MappingTarget Person person);
}
在这个例子中,updatePersonFromDto方法将PersonDto的属性更新到已存在的Person对象。
忽略某些字段
有时候,你可能想要忽略源对象中的某些字段。你可以使用@Mapping注解的ignore参数来实现这一点:
@Mapper
public interface PersonMapper {
@Mapping(target = "seatCount", ignore = true)
PersonDto personToPersonDto(Person person);
}
在这个例子中,personToPersonDto方法将忽略Person的seatCount字段。
使用Builder模式
如果你的目标对象使用了Builder模式,MapStruct也能很好地支持。你只需要在@Mapper注解中指定builder的类名:
@Mapper(builder = @Builder(builderClassName = "Builder", buildMethodName = "build"))
public interface PersonMapper {
PersonDto personToPersonDto(Person person);
}
默认值映射
MapStruct也支持默认值映射,你可以使用@Mapping注解的defaultValue参数来实现这一点:
@Mapper
public interface PersonMapper {
@Mapping(target = "seatCount", source = "numberOfSeats", defaultValue = "4")
PersonDto personToPersonDto(Person person);
}
在这个例子中,如果Person的numberOfSeats属性为null,那么personToPersonDto方法会将PersonDto的seatCount字段设置为"4"。
使用@Qualifier自定义映射方法选择
@Qualifier 注解用于标识自定义转换器方法和映射过程中的限定符。
通过使用 @Qualifier 注解,你可以为转换器方法或映射方法提供更具体的选择标准,以决定哪个方法应该被调用。
下面是一个示例,展示了如何在 MapStruct 1.5 中使用 @Qualifier 注解:
@Mapper
public interface PersonMapper {
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface FastPerson {}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ElectricPerson {}
// 转换器方法1
@FastPerson
PersonDto convertToFastPersonDto(Person person);
// 转换器方法2
@ElectricPerson
PersonDto convertToElectricPersonDto(Person person);
// 映射方法
@Mapping(target = "personDto", qualifiedBy = {FastPerson.class, ElectricPerson.class})
GarageDto mapGarageToDto(Garage garage);
}
在这个示例中,我们定义了两个注解 @FastPerson 和 @ElectricPerson,它们都是通过 @Qualifier 注解来定义的。这些注解用于标识转换器方法 convertToFastPersonDto 和 convertToElectricPersonDto。
然后,在映射方法 mapGarageToDto 上,我们使用了 qualifiedBy 参数来标记多个限定符。通过这样配置,MapStruct 将根据指定的限定符选择适当的转换器方法来进行映射。
请注意,@Qualifier 注解需要与自定义转换器方法一起使用。你可以根据实际需求定义自己的限定符注解,并将其应用于合适的转换器方法上。
全局映射
使用@MapperConfig配置全局映射策略
@MapperConfig 注解在 MapStruct 中用于定义全局或共享的映射配置。这个注解可以被应用在接口或抽象类上,然后其他的 @Mapper 可以通过 config 属性引用这个配置。
下面是一个简单的例子:
首先,我们定义一个全局的映射配置:
@MapperConfig(componentModel = "spring", uses = DateMapper.class)
public interface GlobalMapperConfig {
}
在这个例子中,我们指定了 componentModel 为 “spring”,这意味着生成的映射器将是 Spring 的组件,可以使用 @Autowired 进行注入。我们还指定了 uses 属性为 DateMapper.class,这意味着所有引用这个配置的映射器都可以使用 DateMapper 中定义的方法进行映射。
然后,我们创建一个映射器并引用这个全局配置:
@Mapper(config = GlobalMapperConfig.class)
public interface MyMapper {
// mapping methods
}
在这个例子中,MyMapper 将继承 GlobalMapperConfig 中定义的所有配置。
注意,如果 @Mapper 和 @MapperConfig 中都定义了相同的属性,那么 @Mapper 中的属性将会覆盖 @MapperConfig 中的属性。
@MapperConfig 是一种强大的工具,可以帮助你减少重复的配置,并使你的代码更易于维护
与SpringBoot整合
MapStruct
可以与 Spring Boot
无缝集成,通过 MapStruct
自动生成的映射代码,可以方便地在 Spring
的依赖注入系统中使用。为了整合 MapStruct
和 Spring Boot
,需要配置 MapStruct
生成的 Mapper
作为 Spring
的 Bean
。
po类
Person.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person{
private String name;
}
PersonDTO.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class PersonDTO{
private String fullName;
}
Mapper类
PersonMapper.java
@Mapper(componentModel = "spring") // MapStruct 与 Spring 集成
public interface PersonMapper {
// 映射 Person 到 PersonDTO,指定 name 到 fullName 的映射
@Mapping(source = "name", target = "fullName")
PersonDTO toPersonDTO(Person person);
// 反向映射
@Mapping(source = "fullName", target = "name")
Person toPerson(PersonDTO personDTO);
}
测试类
@SpringBootTest
class PersonMapperTest {
@Resource
private PersonMapper personMapper;
// 获取 PersonDTO 示例
@Test
public void getPersonDTO() {
// 创建一个示例实体
Person person = new Person();
person.setName("李四");
// 使用 MapStruct 映射到 DTO
PersonDTO personDTO = personMapper.toPersonDTO(person);
System.out.println(personDTO);
}
@Test
public void getPerson() {
PersonDTO personDTO= new PersonDTO();
personDTO.setFullName("张三");
// 映射 DTO 到实体
Person person = personMapper.toPerson(personDTO);
System.out.println(person);
}
}
还没有人赞赏,快来当第一个赞赏的人吧!
- 2¥
- 5¥
- 10¥
- 20¥
- 50¥
声明:本文为原创文章,版权归信息岛所有,欢迎分享本文,转载请保留出处!