目录
- 背景
- 常用方式
- MapStruct框架
- 1. 引入方式
- 2. 使用方式
- 2.1 声明接口
- 2.2 类型转换
- 2.3 获取转换器
- 官方链接
背景
java分布式系统经常需要做do(数据库访问对象)对象跟dto(业务传输对象)。一般do对象只涉及系统内部跟数据库的交互,如果跟其他系统通过rpc交互,需要定义dto对象。
但是do对象跟dto对象有很多字段的名称和类型都是相同的,但是需要程序来做转换。
目前常用的方式并发量高的时候,都会有点性能问题。
常用方式
有很多博文专门写过,主要就是一些BeanUtils,常用的如下:
- spring包中的 BeanUtil
- cglib包中的 Beancopier
- Apache BeanUtils
- FastJson的转换
上面的几种方式比较常见,下面看下fastJson的方式。
/**
* 对象映射
* 按照指定的目标class创建一个目标对象,然后将源对象中同名称的属性映射到新的目标对象并返回
*
* @param sourceObject 源对象
* @param targetClass 目标class
* @param <E> 泛型
* @return 目标对象
*/
public static <E> E mapObject(Object sourceObject, Class<E> targetClass) {
String jsonString = JSONObject.toJSONString(sourceObject);
return JSONObject.parseObject(jsonString, targetClass);
}
具体的性能对比,可以参考别人的文章。链接
这几种方式有的通过反射,有的通过字节码生成,如果并发量比较高的情况下,会有些性能问题。
MapStruct框架
mapStruct是基于java注解处理器的实现方式,使用方只需要声明接口,框架在**编译期**负责实现。基于对象属性的get/set方法来做属性映射,所以性能是最高的。这意味着这些值是通过简单的getter / setter调用而不是反射或类似方法从源复制到目标的。
对于名称和类型兼容的属性,mapStruct会自动映射,很方便。对于不能直接映射的类型,可以通过配置的方式做定制化的映射。
1. 引入方式
基于maven的java项目,只需要引入jar包和打包插件即可。
如果跟lombok搭配的话,需要按照指定的方式配置,否则可能会出现异常。
...
<properties>
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2. 使用方式
2.1 声明接口
声明接口,通过@Mapper注解。@Mapper注释使MapStruct代码生成器在构建时创建CarMapper接口的实现。
@Mapper
public interface CarMapper {
@Mapping(source = "make", target = "manufacturer")
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
@Mapping(source = "name", target = "fullName")
PersonDto personToPersonDto(Person person);
}
框架自动生成的代码
// GENERATED CODE
public class CarMapperImpl implements CarMapper {
@Override
public CarDto carToCarDto(Car car) {
if ( car == null ) {
return null;
}
CarDto carDto = new CarDto();
if ( car.getFeatures() != null ) {
carDto.setFeatures( new ArrayList<String>( car.getFeatures() ) );
}
carDto.setManufacturer( car.getMake() );
carDto.setSeatCount( car.getNumberOfSeats() );
carDto.setDriver( personToPersonDto( car.getDriver() ) );
carDto.setPrice( String.valueOf( car.getPrice() ) );
if ( car.getCategory() != null ) {
carDto.setCategory( car.getCategory().toString() );
}
carDto.setEngine( engineToEngineDto( car.getEngine() ) );
return carDto;
}
@Override
public PersonDto personToPersonDto(Person person) {
//...
}
private EngineDto engineToEngineDto(Engine engine) {
if ( engine == null ) {
return null;
}
EngineDto engineDto = new EngineDto();
engineDto.setHorsePower(engine.getHorsePower());
engineDto.setFuel(engine.getFuel());
return engineDto;
}
}
2.2 类型转换
一、 对于相同类型的属性,mapstrct会自动映射。对于不同类型的属性,mapStruct会做简单的类型转换。
- java基本类型,int <–> Integer , bool <–> Boolean等。
- java数字类型: int 、Long 、 Byte
- java基本类型和String的转换
- 枚举类型 and String.
- java.util.Date/XMLGregorianCalendar and String。可通过
配置日期表达式,来做日期和字符串的转换。@Mapping(source = “manufacturingDate”, dateFormat = “dd.MM.yyyy”) - 以及很多的日期格式和类型转换。
二、对象类型的映射,需要映射的对象属性不是java基本属性,是业务系统定义的对象。 这时可以自定义转换方法(比如自动生成代码中的personToPersonDto),如果找不到自定义的转换方法,mapStruct会自动生成映射方法(比如自动生成代码中的engineToEngineDto)。
三、集合类型的属性Collection or Map。
集合类型如果具有相同的元素类型:通过创建包含源属性中元素的目标集合类型的新实例,将复制具有相同元素类型的集合类型的属性。实现类中的carDto.setFeatures( new ArrayList( car.getFeatures() ) );
对于具有不同元素类型的集合类型的属性,每个元素将被单独映射并添加到目标集合中。
四、自定义映射。通过@Mapping注解可以配置自定义映射。
2.3 获取转换器
- 工厂模式
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
CarDto carToCarDto(Car car);
}
- spring 模式
声明Mapper时加上componentModel属性。
@Mapper(componentModel = "cdi")
public interface CarMapper {
CarDto carToCarDto(Car car);
}
通过spring的@Autowired可以获取到实现类了。
@Autowired
private CarMapper mapper;
官方链接
官方文档写的很清晰,有疑问可以看文档。