MapStruct教程-三種方式處理繼承關(guān)系
你好,我是看山。
MapStruct是一個(gè)效率工具,可以在處理Java Bean映射時(shí),幫助我們盡量減少樣板代碼,只需要定義接口,它會(huì)自動(dòng)生成映射邏輯。本文中,我們一起看下如何通過MapStruct處理集成關(guān)系。
我們將討論三種方法:
- 通過實(shí)例檢查;
- 使用訪問者模式;
- 【推薦】使用@SubclassMapping注解。
一、背景
默認(rèn)情況下,MapStruct無法為所有從基類或接口繼承的類生成映射器,不支持運(yùn)行時(shí)識(shí)別實(shí)例和對象層次結(jié)構(gòu)。
(一)基礎(chǔ)定義
先定義POJO類:
public abstract class Vehicle {
private String color;
private String speed;
}
public class Car extends Vehicle{
private Integer tires;
}
public class Bus extends Vehicle {
private Integer capacity;
}
再定義對應(yīng)的DTO類:
public class VehicleDTO {
private String color;
private String speed;
}
public class CarDTO extends VehicleDTO{
private Integer tires;
}
public class BusDTO extends VehicleDTO{
private Integer capacity;
}
(二)定義映射器
這里我們定義三個(gè)映射器:CarMapper、BusMapper、VehicleMapper:
@Mapper
public interface CarMapper {
CarDTO carToDTO(Car car);
}
@Mapper
public interface BusMapper {
BusDTO busToDTO(Bus bus);
}
@Mapper(uses = {BusMapper.class, CarMapper.class})
public interface VehicleMapper {
VehicleDTO vehicleToDTO(Vehicle vehicle);
}
在這里,我們分別定義了所有子類的映射器,并在基類映射器通過uses配置使用它們。
(三)識(shí)別問題
我們先看下VehicleMapper的生成類:
public class VehicleMapperImpl implements VehicleMapper {
@Override
public VehicleDTO vehicleToDTO(Vehicle vehicle) {
if ( vehicle == null ) {
return null;
}
VehicleDTO vehicleDTO = new VehicleDTO();
vehicleDTO.setColor( vehicle.getColor() );
vehicleDTO.setSpeed( vehicle.getSpeed() );
return vehicleDTO;
}
}
可以看到,代碼中是直接使用了VehicleDTO,并沒有用到子類的任何定義。我們可以在入口傳入Car或Bus的實(shí)例對象,但是最終得到的只能是VehicleDTO的實(shí)例。
二、通過實(shí)例檢查實(shí)現(xiàn)
我們一起看看如何通過實(shí)例檢查實(shí)現(xiàn)映射邏輯,因?yàn)楸容^簡單,直接代碼:
@Mapper
public interface VehicleMapperByInstanceChecks {
CarDTO map(Car car);
BusDTO map(Bus bus);
default VehicleDTO mapToVehicleDTO(Vehicle vehicle) {
if (vehicle instanceof Bus) {
return map((Bus) vehicle);
} else if (vehicle instanceof Car) {
return map((Car) vehicle);
} else {
return null;
}
}
}
從上面代碼來說,其實(shí)完全是靠人工智能實(shí)現(xiàn)的,通過我們HardCode,借助instanceof實(shí)現(xiàn)類型判斷,轉(zhuǎn)換為需要的類型實(shí)例。
三、通過訪問者模式實(shí)現(xiàn)
第二種方式是借助訪問者模式實(shí)現(xiàn),通過Java的多態(tài),可以精準(zhǔn)的調(diào)用到需要的方法。
(一)應(yīng)用訪問者模式
首先在抽象類Vehicle中定義抽象方法accept(),以接受任何訪問者對象:
public abstract class Vehicle {
public abstract VehicleDTO accept(Visitor visitor);
}
public interface Visitor {
VehicleDTO visit(Car car);
VehicleDTO visit(Bus bus);
}
現(xiàn)在,我們需要為每個(gè)Vehicle子類實(shí)現(xiàn)accept()方法:
public class Bus extends Vehicle {
@Override
VehicleDTO accept(Visitor visitor) {
return visitor.visit(this);
}
}
public class Car extends Vehicle {
@Override
VehicleDTO accept(Visitor visitor) {
return visitor.visit(this);
}
}
最后,我們可以通過實(shí)現(xiàn)訪問者接口來實(shí)現(xiàn)映射器:
@Mapper
public abstract class VehicleMapperByVisitorPattern implements Visitor {
public VehicleDTO mapToVehicleDTO(Vehicle vehicle) {
return vehicle.accept(this);
}
@Override
public VehicleDTO visit(Car car) {
return map(car);
}
@Override
public VehicleDTO visit(Bus bus) {
return map(bus);
}
abstract CarDTO map(Car car);
abstract BusDTO map(Bus bus);
}
從性能來說,訪問者模式方法比實(shí)例檢查方法更優(yōu)化,借助Java的多態(tài)實(shí)現(xiàn),快速定位轉(zhuǎn)換方法。從代碼量來說,實(shí)例檢查會(huì)由于訪問者模式,因?yàn)閷懙纳佟?/p>
其實(shí)這里會(huì)引出一個(gè)問題,設(shè)計(jì)模式是好是壞?
四、使用@SubclassMapping實(shí)現(xiàn)
MapStruct提供了@SubclassMapping注解,允許我們配置的映射器,可以處理有繼承關(guān)系的類實(shí)例轉(zhuǎn)換。
@SubclassMapping注解中有source和target兩個(gè)配置,source定義要映射的源子類,target定義要映射到的目標(biāo)子類:
(一)定義
我們使用@SubclassMapping注解指定繼承對應(yīng)關(guān)系:
@Mapper(uses = {BusMapper.class, CarMapper.class})
public interface VehicleMapperBySubclassMapping {
@SubclassMapping(source = Car.class, target = CarDTO.class)
@SubclassMapping(source = Bus.class, target = BusDTO.class)
VehicleDTO mapToVehicleDTO(Vehicle vehicle);
}
生成的映射代碼為:
public class VehicleMapperBySubclassMappingImpl implements VehicleMapperBySubclassMapping {
private final BusMapper busMapper = Mappers.getMapper( BusMapper.class );
private final CarMapper carMapper = Mappers.getMapper( CarMapper.class );
@Override
public VehicleDTO mapToVehicleDTO(Vehicle vehicle) {
if ( vehicle == null ) {
return null;
}
if (vehicle instanceof Car) {
return carMapper.carToDTO( (Car) vehicle );
}
else if (vehicle instanceof Bus) {
return busMapper.busToDTO( (Bus) vehicle );
}
else {
VehicleDTO vehicleDTO = new VehicleDTO();
vehicleDTO.setColor( vehicle.getColor() );
vehicleDTO.setSpeed( vehicle.getSpeed() );
return vehicleDTO;
}
}
}
可以看到,MapStruct自動(dòng)實(shí)現(xiàn)了實(shí)例檢查,通過instanceof判斷類型。