CarDTO entity = JSON.parseObject(JSON.toJSONString(carDO), CarDTO.class);
org.apache.commons.beanutils.BeanUtils.copyProperties(do, entity);
org.springframework.beans.BeanUtils.copyProperties(do, entity);
BeanCopier copier = BeanCopier.create(CarDO.class, CarDTO.class, false);
copier.copy(do, dto, null);
运行期反射调用 set/get 或者是直接对成员变量赋值。这种方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。运行期对象转换的代表主要是Dozer和ModelMaper。
编译期动态生成 set/get 代码的class文件,在运行时直接调用该class的 set/get 方法。该方式实际上仍会存在 set/get 代码,只是不需要开发人员自己写了。这类的代表是:MapStruct,Selma,Orika。
无论哪种Mapping框架,基本都是采用xml配置文件 or 注解的方式供用户配置,然后生成映射关系。
编译期生成class文件方式需要DTO仍然有set/get方法,只是调用被屏蔽;而运行期反射方式在某些直接填充 field的方案中,set/get代码也可以省略。
编译期生成class方式会有源代码在本地,方便排查问题。
编译期生成class方式因为在编译期才出现java和class文件,所以热部署会受到一定影响。
反射型由于很多内容是黑盒,在排查问题时,不如编译期生成class方式方便。参考GitHub上工程java-object-mapper-benchmark可以看出主要框架性能比较。
反射型调用由于是在运行期根据映射关系反射执行,其执行速度会明显下降N个量级。
通过编译期生成class代码的方式,本质跟直接写代码区别不大,但由于代码都是靠模板生成,所以代码质量没有手工写那么高,这也会造成一定的性能损失。
...
<properties>
<org.mapstruct.version>1.4.2.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.8.1</version>
<configuration>
<source>1.8</source>
<!-- depending on your project -->
<target>1.8</target>
<!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
}
public class CarDTO {
private String make;
private int seatCount;
private String type;
}
当属性与其目标实体副本同名时,它将被隐式映射。
当目标实体中的属性具有不同名称时,可以通过@Mapping注释指定其名称。
public interface CarMapper {
"numberOfSeats", target = "seatCount") (source =
CarDTO CarToCarDTO(Car car); }
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car);
}
Car car = new Car(...);
CarDTO carDTO = CarMapper.INSTANCE.CarToCarDTO(car);
public interface CarMapper {
CarDTO CarToCarDTO(Car car);
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
"numberOfSeats", target = "seatCount") (source =
CarDTO CarToCarDTO(Car car);
Car CarDTOToCar(CarDTO carDTO);
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
void updateDTOFromCar(Car car, CarDTO carDTO);
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
"numberOfSeats", target = "seatCount") (source =
CarDTO CarToCarDTO(Car car);
List<CarDTO> carsToCarDtos(List<Car> cars);
Set<String> integerSetToStringSet(Set<Integer> integers);
"dd.MM.yyyy") (valueDateFormat =
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car, Person person); }
编译生成的代码:
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
Target sourceToTarget(Source s);
}
可以在Mapper中定义默认实现方法,生成转换代码将调用相关方法:
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car);
default String getLengthType(int length) {
if (length > 5) {
return "large";
} else {
return "small";
}
}
}
public class DateMapper {
public String asString(Date date) {
return date != null ? new SimpleDateFormat( "yyyy-MM-dd" ).format( date ) : null;
}
public Date asDate(String date) {
try {
return date != null ? new SimpleDateFormat( "yyyy-MM-dd" ).parse( date ) : null;
} catch ( ParseException e ) {
throw new RuntimeException( e );
}
}
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car);
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car);
default String getLengthType(int length) {
if (length > 5) {
return "large";
} else {
return "small";
}
}
default String getLengthType2(int length) {
if (length > 7) {
return "large";
} else {
return "small";
}
}
}
表达式自定义映射
public class Driver {
private String name;
private int age;
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car, Person person);
}
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
Target sourceToTarget(Source s);
}
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
CarDTO CarToCarDTO(Car car);
}
public abstract class CarMapperDecorator implements CarMapper {
private final CarMapper delegate;
protected CarMapperDecorator(CarMapper delegate) {
this.delegate = delegate;
}
@Override
public CarDTO CarToCarDTO(Car car) {
CarDTO dto = delegate.CarToCarDTO(car);
dto.setMakeInfo(car.getMake() + " " + new SimpleDateFormat( "yyyy-MM-dd" ).format(car.getCreateDate()));
return dto;
}
}
技术公开课
Java高级编程
本课程共162课时,包含Java多线程编程、常用类库、IO编程、网络编程、类集框架、JDBC等实用开发技术,帮助同学们掌握系统提供的类库并熟练使用JavaDoc文档。同时考虑到对面向对象的理解以及常用类的设计模式,在课程讲解中还将进行源代码的使用分析与结构分析。