使用MapStruct进行Java Bean映射的详细指南
简介
MapStruct 是一个用于 Java Bean 映射的注解处理器。它通过注解生成类型安全且性能优异的映射代码,避免手动编写重复的样板代码,适用于将一个 Java Bean 类型转换为另一个 Java Bean 类型。
MapStruct的主要特性
- 类型安全: 在编译时生成映射代码,避免运行时错误。
- 高性能: 生成的代码是纯 Java 代码,没有反射机制,性能非常高。
- 简洁: 减少了大量样板代码,开发者只需定义接口,MapStruct 自动生成实现。
- 可定制性: 支持自定义映射、默认值、转换方法等。
依赖配置
使用 MapStruct 需要添加相应的依赖。以 Maven 为例:
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.0.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
如果使用 Gradle:
dependencies {
implementation 'org.mapstruct:mapstruct:1.5.0.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.0.Final'
}
MapStruct的使用步骤
-
定义映射接口
创建一个接口,并使用
@Mapper
注解标记它。import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; import org.mapstruct.factory.Mappers; /** * CarMapper 接口定义了 Car 和 CarDto 之间的映射关系。 * 使用 @Mapper 注解标记接口,并设置 typeConversionPolicy 为 ReportingPolicy.ERROR, * 以确保类型转换不正确时编译报错。 */ @Mapper(typeConversionPolicy = ReportingPolicy.ERROR) public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); /** * 将 Car 实体映射为 CarDto。 * @param car 源 Car 对象 * @return 映射后的 CarDto 对象 */ @Mapping(source = "numberOfSeats", target = "seatCount") @Mapping(source = "engine.type", target = "engineType") CarDto carToCarDto(Car car); }
-
定义源类和目标类
/** * Car 实体类 */ public class Car { private String make; // 制造商 private int numberOfSeats; // 座位数 private Engine engine; // 引擎 // getters 和 setters } /** * CarDto 数据传输对象 */ public class CarDto { private String make; // 制造商 private int seatCount; // 座位数 private String engineType; // 引擎类型 // getters 和 setters } /** * Engine 实体类 */ public class Engine { private String type; // 引擎类型 // getters 和 setters }
-
生成映射代码
使用构建工具(如 Maven 或 Gradle)执行构建过程,MapStruct 将根据接口定义生成相应的映射实现类。
/** * CarMapper 的实现类,由 MapStruct 自动生成。 */ public class CarMapperImpl implements CarMapper { @Override public CarDto carToCarDto(Car car) { if (car == null) { return null; } CarDto carDto = new CarDto(); carDto.setMake(car.getMake()); carDto.setSeatCount(car.getNumberOfSeats()); if (car.getEngine() != null) { carDto.setEngineType(car.getEngine().getType()); } return carDto; } }
-
调用映射方法
/** * 演示如何使用 CarMapper 将 Car 实体映射为 CarDto。 */ public class Main { public static void main(String[] args) { Car car = new Car(); car.setMake("Toyota"); car.setNumberOfSeats(5); Engine engine = new Engine(); engine.setType("V8"); car.setEngine(engine); CarDto carDto = CarMapper.INSTANCE.carToCarDto(car); System.out.println(carDto); } }
深拷贝和浅拷贝
浅拷贝: 复制对象的基本数据类型属性和引用类型属性的引用,引用类型属性本身并没有被复制。
深拷贝: 复制对象的所有属性,包括引用类型属性所引用的对象本身。
MapStruct 默认进行浅拷贝,即引用类型属性在目标对象中会引用源对象的相同实例。如果需要进行深拷贝,可以手动编写自定义映射方法。
集合拷贝
MapStruct 可以自动处理集合类型的映射。例如:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* CarMapper 接口定义了 Car 和 CarDto 之间的映射关系。
* 使用 @Mapper 注解标记接口,并设置 typeConversionPolicy 为 ReportingPolicy.ERROR,
* 以确保类型转换不正确时编译报错。
*/
@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 将 Car 实体映射为 CarDto。
* @param car 源 Car 对象
* @return 映射后的 CarDto 对象
*/
@Mapping(source = "numberOfSeats", target = "seatCount")
@Mapping(source = "engine.type", target = "engineType")
CarDto carToCarDto(Car car);
/**
* 将 Car 实体列表映射为 CarDto 列表。
* @param cars 源 Car 对象列表
* @return 映射后的 CarDto 对象列表
*/
List<CarDto> carsToCarDtos(List<Car> cars);
}
编译时错误处理
MapStruct 在编译时进行类型检查,如果映射不正确,会抛出编译错误。可以使用 @Mapper
注解的 typeConversionPolicy
属性来配置在类型转换过程中遇到错误时的行为,例如 ReportingPolicy.ERROR
。
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
/**
* CarMapper 接口定义了 Car 和 CarDto 之间的映射关系。
* 使用 @Mapper 注解标记接口,并设置 typeConversionPolicy 为 ReportingPolicy.ERROR,
* 以确保类型转换不正确时编译报错。
*/
@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 将 Car 实体映射为 CarDto。
* @param car 源 Car 对象
* @return 映射后的 CarDto 对象
*/
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
在上述示例中,如果类型转换不正确或者无法转换,编译时将会报错,从而确保所有类型转换都得到正确处理。
完整示例与文档生成
以下是一个完整的示例,包括源代码和生成的映射代码,以及如何生成文档。
源代码
源类和目标类:
/**
* Car 实体类
*/
public class Car {
private String make; // 制造商
private int numberOfSeats; // 座位数
private Engine engine; // 引擎
// getters 和 setters
}
/**
* CarDto 数据传输对象
*/
public class CarDto {
private String make; // 制造商
private int seatCount; // 座位数
private String engineType; // 引擎类型
// getters 和 setters
}
/**
* Engine 实体类
*/
public class Engine {
private String type; // 引擎类型
// getters 和 setters
}
映射接口:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* CarMapper 接口定义了 Car 和 CarDto 之间的映射关系。
* 使用 @Mapper 注解标记接口,并设置 typeConversionPolicy 为 ReportingPolicy.ERROR,
* 以确保类型转换不正确时编译报错。
*/
@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
/**
* 将 Car 实
体映射为 CarDto。
* @param car 源 Car 对象
* @return 映射后的 CarDto 对象
*/
@Mapping(source = "numberOfSeats", target = "seatCount")
@Mapping(source = "engine.type", target = "engineType")
CarDto carToCarDto(Car car);
/**
* 将 Car 实体列表映射为 CarDto 列表。
* @param cars 源 Car 对象列表
* @return 映射后的 CarDto 对象列表
*/
List<CarDto> carsToCarDtos(List<Car> cars);
}
生成的映射代码
MapStruct 将自动生成如下实现类:
/**
* CarMapper 的实现类,由 MapStruct 自动生成。
*/
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car) {
if (car == null) {
return null;
}
CarDto carDto = new CarDto();
carDto.setMake(car.getMake());
carDto.setSeatCount(car.getNumberOfSeats());
if (car.getEngine() != null) {
carDto.setEngineType(car.getEngine().getType());
}
return carDto;
}
@Override
public List<CarDto> carsToCarDtos(List<Car> cars) {
if (cars == null) {
return null;
}
List<CarDto> list = new ArrayList<>(cars.size());
for (Car car : cars) {
list.add(carToCarDto(car));
}
return list;
}
}
生成文档
可以使用 Javadoc 工具生成文档,示例如下:
javadoc -d doc -sourcepath src/main/java -subpackages com.example
生成的文档将包含所有类和接口的详细说明,包括字段、方法和注释。
总结
通过本指南,你可以了解 MapStruct 的基本特性、如何配置依赖、使用步骤、深拷贝和浅拷贝的区别、集合类型的映射以及如何处理编译时错误。MapStruct 能极大地提高代码的可维护性和性能,是进行 Java Bean 映射的有力工具。