MapStructPlus使用教程

MapStructPlus是Mapstruct的扩展,它提供自动Mapper接口生成和增强功能,使得Java类型之间的转换更简单。用户只需在类上添加@AutoMapper注解,指定目标类型,即可自动生成转换逻辑。此外,MapStructPlus还支持与Lombok的整合,以及自定义转换器和属性转换规则,包括时间格式、数字格式、默认值和表达式转换等。
摘要由CSDN通过智能技术生成

MapStruct Plus

image-20230718231529468

简介

首先,先了解一下 Mapstruct: Mapstruct 是一个代码生成器,通过定义类转换的接口,自动实现属性转换的具体逻辑。主要为了简化 Java 类型之间转换的实现

Mapstruct Plus 是 Mapstruct 的增强工具,在 Mapstruct 的基础上,实现了自动生成 Mapper 接口的功能,并强化了部分功能,使 Java 类型转换更加便捷、优雅。

和 Mapstruct 一样,本质上都是一个基于 JSR 269 的 Java 注释处理器,因此可以由 Maven、Gradle、Ant 等来构建触发。

Mapstruct Plus 内嵌 Mapstruct,和 Mapstruct 完全兼容,如果之前已经使用 Mapstruct,可以无缝替换依赖。

快速开始(Springboot环境)

引入依赖

<properties>
    <mapstruct-plus.version>最新版本</mapstruct-plus.version>
</properties>
<dependencies>
    <dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
        <version>${mapstruct-plus.version}</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>io.github.linpeilie</groupId>
                        <artifactId>mapstruct-plus-processor</artifactId>
                        <version>${mapstruct-plus.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

注意点

注意点1:依赖

​ 由于其已经内嵌 Mapstruct,为了防止不同版本之间的差异,请不要再引入 Mapstruct 相关依赖

注意点2:与lombok整合

与 Mapstruct 整合 lombok 的方式一致。

lombok 1.18.16 之前:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>io.github.linpeilie</groupId>
                        <artifactId>mapstruct-plus-processor</artifactId>
                        <version>${mapstruct-plus.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
lombok 1.18.16 及以后:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>io.github.linpeilie</groupId>
                        <artifactId>mapstruct-plus-processor</artifactId>
                        <version>${mapstruct-plus.version}</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

下面演示如何使用 MapStruct Plus 来映射两个对象。

假设有两个类 UserDtoUser,分别表示数据层对象和业务层对象:

@Data
@AllArgsConstructor
@NoArgsConstructor
@AutoMapper(target = UserDto.class)
public class User {
    private String username;
    private int age;
    private boolean young;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
    private String username;
    private int age;
    private boolean young;
}

指定对象映射关系

User 或者 UserDto 上面增加注解 —— @AutoMapper,并设置 targetType 为对方类。

@Data
@AllArgsConstructor
@NoArgsConstructor
@AutoMapper(target = UserDto.class)
public class User {
    private String username;
    private int age;
}

自动生成的转换实现类

image-20230718231417831

测试

@SpringBootTest
public class QuickStartTest {

    private static Converter converter = new Converter();

    public static void main(String[] args) {
        User user = new User();
        user.setUsername("jack");
        user.setAge(23);
        user.setYoung(false);

        UserDto userDto = converter.convert(user, UserDto.class);
        System.out.println(userDto);    // UserDto{username='jack', age=23, young=false}

        assert user.getUsername().equals(userDto.getUsername());
        assert user.getAge() == userDto.getAge();
        assert user.isYoung() == userDto.isYoung();

        User newUser = converter.convert(userDto, User.class);

        System.out.println(newUser);    // User{username='jack', age=23, young=false}

        assert user.getUsername().equals(newUser.getUsername());
        assert user.getAge() == newUser.getAge();
        assert user.isYoung() == newUser.isYoung();
    }

}

指南

两个类之间的转换

简单转换

要实现两个类之间的转换,只需要在其中一个类上增加注解 @AutoMapper ,配置 target 属性,指定目标类即可

例如:

Car

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class)
public class Car {
    private String name;
    private String type;
}

CarDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CarDto {
    private String name;
    private String type;
}

测试

@SpringBootTest
public class SimpleConvertTest {

    private Converter converter = new Converter();

    @Test
    public void test() {
        Car car = new Car().setName("宝马").setType("小轿车");
        CarDto carDto = this.converter.convert(car, CarDto.class);
        System.out.println("carDto = " + carDto); // carDto = CarDto(name=宝马, type=小轿车)
    }

}

自动生成的转换实现类

image-20230718230223596

该例子表示,会生成 Car 转换为 CarDto 的接口 CarToCarDtoMapper 及实现类 CarToCarDtoMapperImpl。在生成的转换代码中,源类型(Car)的所有可读属性将被复制到目标属性类型(CarDto)的相应属性中。

当一个属性与它的目标实体对应物具有相同的名称时,将会被隐式映射。

除此之外,Mapstruct Plus 会根据当前的默认规则,生成 CarDto 转换为 Car 的接口 CarDtoToCarMapper 及实现类 CarDtoToCarMapperImpl。如果不想生成该转换逻辑的话,可以通过注解的 reverseConvertGenerate 属性来配置。

对象的属性自动转换

当要转换的类中,存在自定义类时,会自动寻找该类型的转换方法。

例如,分别有两组对象模型:汽车(Car)和座椅(SeatConfiguration),其中 Car 依赖于 SeatConfiguration

分别对应对象如下:

Car

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class)
public class Car {
    private String name;
    private String type;
    private SeatConfiguration seatConfiguration;
}

CarDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CarDto {
    private String name;
    private String type;
    private SeatConfiguration seatConfiguration;
}

SeatConfiguration

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = SeatConfigurationDto.class)
public class SeatConfiguration {
    private String name;
    private Integer price;
}

SeatConfigurationDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SeatConfigurationDto {
    private String name;
    private Integer price;
}

测试对象属性转换

@SpringBootTest
public class ObjectConvertTest {

    private Converter converter = new Converter();

    @Test
    public void test() {
        SeatConfiguration seatConfiguration = new SeatConfiguration().setName("真皮座椅").setPrice(5000);
        Car car = new Car().setName("宝马").setType("小轿车").setSeatConfiguration(seatConfiguration);
        CarDto carDto = this.converter.convert(car, CarDto.class);
        System.out.println("carDto = " + carDto); 
        // carDto = CarDto(name=宝马, type=小轿车, seatConfiguration=SeatConfiguration(name=真皮座椅, price=5000))
    }

}

自动生成的转换实现类

image-20230718230152743

在上面的例子中,首先会生成 CarToCarDtoMapperSeatConfigurationToSeatConfigurationDtoMapper 两个转换接口,并且在转换 Car 时,会自动使用 SeatConfigurationToSeatConfigurationDtoMapper 来对其中的座椅属性来进行转换。

引入自定义类型转换器

当不同类型的属性,想要按照自定义的规则进行转换时,可以有两种办法:

  1. 通过 @AutoMapping 中配置的 expression 表达式配置
  2. 自定义一个类型转换器,通过 @AutoMapperuses 属性来引入

方式一可以参考下面的表达式章节。

这里基于方式二,实现将 String 类型的属性,根据逗号分隔,转换为 List<String> 类型的属性:

首先,定义一个类型转换器 —— StringToListStringConverter

StringToListStringConverter

@Component
public class StringToListStringConverter {
    public static List<String> stringToListString(String str) {
        return StrUtil.split(str);
    }
}

User

@Data
@AllArgsConstructor
@NoArgsConstructor
@AutoMapper(target = UserDto.class)
public class User {
    private String username;
    private int age;
    private boolean young;
    private List<String> educationList;
}

UserDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = User.class, uses = StringToListStringConverter.class)
public class UserDto {
    private String username;
    private int age;
    private boolean young;
    @AutoMapping(target = "educationList")
    private String educations;
}

新增MapstructPlusConfig配置类,注入Converter,方便后续使用

@Configuration
public class MapstructPlusConfig {
    @Bean
    public Converter converter() {
        return new Converter();
    }
}

测试自定义类型转换器(StringToListString)

@SpringBootTest
public class CustomerConverterTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        UserDto userDto = new UserDto().setAge(23).setUsername("张三").setYoung(true).setEducations("1,2,3");

        User user = converter.convert(userDto, User.class);
        System.out.println(user); // User(username=张三, age=23, young=true, educationList=[1, 2, 3])
    }

}

自定义属性转换

当两个类中属性存在不一致的场景时,例如名称、类型等不一致,可以进行自定义转换,通过在属性上面添加 @AutoMapping,来配置映射规则。

不同属性名称映射

@AutoMapping 注解中,提供了 target 属性,可以配置当前属性与目标类中 target 属性之间映射。

例如,Book 转换为 BookDto 时,name 属性与 bookName 属性相对应:

Book

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = BookDto.class)
public class Book {
    @AutoMapping(target = "bookName")
    private String name;
}

BookDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BookDto {
    private String bookName;
}

测试不同属性名称映射

@SpringBootTest
public class AttributeConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        Book book = new Book().setName("三国演义");
        BookDto bookDto = this.converter.convert(book, BookDto.class);
        System.out.println(bookDto); // BookDto(bookName=三国演义)
    }

}

@AutoMapping 注解中还提供 source 方法,该配置默认取当前属性的名称,之所以可以配置,是为了适应一种场景,当前类的某个属性,其内部的属性,转换为目标中的属性字段,则可以通过当前属性来配置。

例如:

Goods

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = GoodsVo.class, reverseConvertGenerate = false)
public class Goods {

    @AutoMapping(source = "sku.price", target = "price")
    private Sku sku;

}

GoodsDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class GoodsVo {
    private Integer price;
}

Sku

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Sku {
    private String name;
    private Integer price;
}

测试AutoMapping的source属性

@SpringBootTest
public class AttributeConvertTest {

    @Resource
    private Converter converter;
    
    @Test
    public void test2() {
        Sku sku = new Sku().setName("手机").setPrice(4999);
        Goods goods = new Goods().setSku(sku);
        GoodsVo goodsVo = this.converter.convert(goods, GoodsVo.class);
        System.out.println(goodsVo); // GoodsVo(price=4999)
    }

}
指定时间格式转换

当时间类型(例如:DateLocalDateTimeLocalDate 等等)需要和 String 通过指定时间格式进行转换时,可以通过 @AutoMapping 中的 dateFormat 来配置:

例如:

Order

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = OrderEntity.class)
public class Order {

    @AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime orderTime;

    @AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
    private Date createTime;

    @AutoMapping(target = "orderDate", dateFormat = "yyyy-MM-dd")
    private String date;

}

OrderEntity

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = Order.class)
public class OrderEntity {

    @AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
    private String orderTime;

    @AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
    private String createTime;

    @AutoMapping(target = "date", dateFormat = "yyyy-MM-dd")
    private LocalDate orderDate;

}

测试时间格式转换

@SpringBootTest
public class TimeConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        Order order = new Order().setOrderTime(LocalDateTime.now()).setCreateTime(new Date()).setDate("2023-07-23");
        OrderEntity orderEntity = this.converter.convert(order, OrderEntity.class);
        System.out.println(orderEntity); // OrderEntity(orderTime=2023-07-23 22:21:00, createTime=2023_07_23 22:21:00, orderDate=2023-07-23)

        OrderEntity orderEntity1 = new OrderEntity().setOrderTime("2023-07-23 22:00:00").setCreateTime("2023_07_23 22:21:00").setOrderDate(LocalDate.now());
        Order order1 = this.converter.convert(orderEntity1, Order.class); // Order(orderTime=2023-07-23T22:00, createTime=Sun Jul 23 22:21:00 CST 2023, date=2023-07-23)
        System.out.println(order1);
    }

}
指定数字格式转换

当数字类型(例如:int/Integer 等数字基本类型及包装类、BigDecimal)和 String 之间的转换需要指定数字格式,可以通过 @AutoMappingnumberFormat 来配置。

该格式需要 java.text.DecimalFormat 所支持

例如:

Order

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = OrderEntity.class)
public class Order {

    @AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime orderTime;

    @AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
    private Date createTime;

    @AutoMapping(target = "orderDate", dateFormat = "yyyy-MM-dd")
    private String date;

    @AutoMapping(numberFormat = "$0.00")
    private BigDecimal orderPrice;

    @AutoMapping(numberFormat = "$0.00")
    private Integer goodsNum;

}

OrderEntity

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = Order.class)
public class OrderEntity {

    @AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
    private String orderTime;

    @AutoMapping(dateFormat = "yyyy_MM_dd HH:mm:ss")
    private String createTime;

    @AutoMapping(target = "date", dateFormat = "yyyy-MM-dd")
    private LocalDate orderDate;

    @AutoMapping(numberFormat = "$0.00")
    private String orderPrice;

    @AutoMapping(numberFormat = "$0.00")
    private String goodsNum;

}

测试数字格式转换

@SpringBootTest
public class NumberConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        Order order = new Order().setOrderPrice(BigDecimal.valueOf(998.99)).setGoodsNum(10000);
        OrderEntity orderEntity = this.converter.convert(order, OrderEntity.class);
        System.out.println(orderEntity); // OrderEntity(orderTime=null, createTime=null, orderDate=null, orderPrice=$998.99, goodsNum=$10000.00)s
    }

}
忽略指定属性的转换

当在进行转换时,需要忽略指定属性的转换,可以通过 @AutoMappingignore 来配置。

例如:

Car

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = CarDto.class)
public class Car {
    private String name;
    @AutoMapping(target = "type", ignore = true)
    private String type;
    private SeatConfiguration seatConfiguration;
}

CarDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class CarDto {
    private String name;
    private String type;
    private SeatConfiguration seatConfiguration;
}

测试忽略指定属性的转换

@SpringBootTest
public class IgnoreConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        SeatConfiguration seatConfiguration = new SeatConfiguration().setName("真皮座椅").setPrice(5000);
        Car car = new Car().setName("宝马").setType("小轿车").setSeatConfiguration(seatConfiguration);
        CarDto carDto = this.converter.convert(car, CarDto.class);
        System.out.println(carDto); // CarDto(name=宝马, type=null, seatConfiguration=SeatConfiguration(name=真皮座椅, price=5000))
    }

}
属性转换时的默认值

@AutoMapping 中的 defaultValue 可以指定在转换属性时,当属性为 null 时,转换到目标类中的默认值。

例如:

DefaultDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = DefaultVo.class)
public class DefaultDto {

    @AutoMapping(defaultValue = "18")
    private Integer i;

    @AutoMapping(defaultValue = "1.32")
    private Double d;

    @AutoMapping(defaultValue = "true")
    private Boolean b;

}

DefaultVo

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class DefaultVo {

    private Integer i;

    private Double d;

    private Boolean b;

}

测试属性转换时的默认值

@SpringBootTest
public class DefaultValueConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        DefaultDto defaultDto = new DefaultDto();
        DefaultVo defaultVo = this.converter.convert(defaultDto, DefaultVo.class);
        System.out.println(defaultVo); // DefaultVo(i=18, d=1.32, b=true)
    }

}
表达式

在执行属性转换时,可以通过指定执行一段 Java 代码来进行转换操作,例如,对源对象中的某个属性进行转换后返回。

需要注意的是,在生成时,会直接将表达式插入到转换逻辑中,并不会验证其语法。

例如,将源对象中的 List<String> 属性,通过 , 拼接为字符串:

User

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = UserDto.class)
public class User {
    private String username;
    private int age;
    private boolean young;
    @AutoMapping(target = "educations", expression = "java(java.lang.String.join(\",\", source.getEducationList()))")
    private List<String> educationList;
}

UserDto

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = User.class, uses = StringToListStringConverter.class)
public class UserDto {
    private String username;
    private int age;
    private boolean young;
    @AutoMapping(target = "educationList")
    private String educations;
}

测试表达式

@SpringBootTest
public class ExpressionConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        User user = new User().setUsername("张三").setAge(23).setYoung(true).setEducationList(Arrays.asList("1", "2", "3"));
        UserDto userDto = this.converter.convert(user, UserDto.class);
        System.out.println(userDto); // UserDto(username=张三, age=23, young=true, educations=1,2,3)
    }

}

自动接入自定义转换接口

当有的类型转换逻辑比较复杂,可以通过自定义转换接口来实现,即使用 MapStruct 原生的方式。

当使用这种方式时,默认生成的类型转换中,如果有前面提供的类型转换时,会自动引用。

例如:

Car

@AutoMapper(target = CarDto.class)
@Data
public class Car {
    private Tyre tyre;
}

CarDto

@Data
public class CarDto {
    private TyreDTO tyre;
}

这里定义 TyreTyreDTO 之间的转换接口:

TyreMapper

@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface TyreMapper {

    TyreDTO tyreToTyreDTO(Tyre tyre);

    Tyre tyreDtoToTyre(TyreDTO tyreDTO);

}

生成的 CarCarDto 转换接口的实现类如下:

CarToCarDtoMapperImpl

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-04-24T15:38:48+0800",
    comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class CarToCarDtoMapperImpl implements CarToCarDtoMapper {

    @Autowired
    private TyreMapper tyreMapper;

    @Override
    public CarDto convert(Car source) {
        if ( source == null ) {
            return null;
        }

        CarDto carDto = new CarDto();

        carDto.setTyre( tyreMapper.tyreToTyreDTO( source.getTyre() ) );

        return carDto;
    }

    @Override
    public CarDto convert(Car source, CarDto target) {
        if ( source == null ) {
            return target;
        }

        target.setTyre( tyreMapper.tyreToTyreDTO( source.getTyre() ) );

        return target;
    }
}

CarDtoToCarMapperImpl

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-04-24T15:38:49+0800",
    comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class CarDtoToCarMapperImpl implements CarDtoToCarMapper {

    @Autowired
    private TyreMapper tyreMapper;

    @Override
    public Car convert(CarDto source) {
        if ( source == null ) {
            return null;
        }

        Car car = new Car();

        car.setTyre( tyreMapper.tyreDtoToTyre( source.getTyre() ) );

        return car;
    }

    @Override
    public Car convert(CarDto source, Car target) {
        if ( source == null ) {
            return target;
        }

        target.setTyre( tyreMapper.tyreDtoToTyre( source.getTyre() ) );

        return target;
    }
}

反向属性映射配置

前面提到,当在一个类上面添加 @AutoMapper 注解时,默认情况下,除了会生成源类到目标类的转换接口,还会生成目标类到源类的转换接口和实现类,这里需要注意的是,默认情况下生成的该转换接口,并没有任何自定义配置,即使在源类中配置了 @AutoMapping 注解。

这里要实现目标类到源类的自定义转换配置,可以有两种方式:

  1. 在目标类上面添加 @AutoMapper 注解。这是最建议的方式,当转换双方都有添加该注解时,便不会生成默认的转换接口,即按照自定义的规则进行生成。
  2. 当目标类访问不到源类,或者项目规范不允许在目标类上面添加该种注解时,可以将自定义配置全部添加在源类中。这就是下面要介绍的反向属性映射配置

框架中提供了 @ReverseAutoMapping 注解,该注解就是为了配置目标类到源类的自定义转换规则。

注意

这里需要注意的是,防止配置冲突,一旦添加 @ReverseAutoMapping 注解,在目标类中,便不能添加任何自定义转换注解

@ReverseAutoMapping 注解表示的含义,是目标类到源类转换时,需要指定的自定义转换规则,其中可以配置的属性,与 @AutoMapping 注解一致。

这里有两个属性需要注意,分别是 sourcetarget

这里的 source 指的是目标类中的属性,target 指的是源类中的属性。

可能会有人这里有疑问,为什么这里的配置像是反的?如果没有,可以直接跳过。

框架设计的时候,所有的属性转换配置,都是基于要转换的类型,该类转换为目标类,想要应用的效果。这里的 source 也应该是来源类中的属性。

如果还是不理解,这里可以认为,该注解就是本该应用在目标类中的 @AutoMapping 注解,原封不动拷贝到当前类,再修改注解名称即可。

例如:

Student 只在源类配置正向和反向转换规则

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@AutoMapper(target = StudentDto.class)
public class Student {
    @AutoMapping(source = "name", target = "sname")
    @ReverseAutoMapping(source = "sname", target = "name")
    private String name;
    @AutoMapping(source = "age", target = "sage")
    @ReverseAutoMapping(source = "sage", target = "age")
    private Integer age;
}

StudentDto 在目标类并未配置转换规则

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class StudentDto {
    private String sname;
    private Integer sage;
}

测试反向属性映射配置

@SpringBootTest
public class ReverseAutoMappingConvertTest {

    @Resource
    private Converter converter;

    @Test
    public void test() {
        Student student = new Student().setName("张三").setAge(23);
        StudentDto studentDto = this.converter.convert(student, StudentDto.class);
        System.out.println(studentDto); // StudentDto(sname=张三, sage=23)

        StudentDto studentDto1 = new StudentDto().setSname("李四").setSage(18);
        Student student1 = this.converter.convert(studentDto1, Student.class);
        System.out.println(student1); // Student(name=李四, age=18)
    }

}

也会自动生成目标类至源类的转换规则

image-20230725231407947

不可变类型设计

当一个类型是不可变类型时,之前默认的规则,生成的 T convert(S source, @MappingTarget T target) 可能会存在问题。

所以,可以使用任意包下的 Immutable 注解,标识一个类为不可变类型, 当为不可变类型时,@MappingTarget 没有意义,上面的方法最终生成如下:

public T convert(S source, @MappingTarget T target) {
    return target;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值