MapStruct教程-三种方式处理继承关系

开发 前端
MapStruct是一个效率工具,可以在处理Java Bean映射时,帮助我们尽量减少样板代码,只需要定义接口,它会自动生成映射逻辑。本文中,我们一起看下如何通过MapStruct处理集成关系。

你好,我是看山。

MapStruct是一个效率工具,可以在处理Java Bean映射时,帮助我们尽量减少样板代码,只需要定义接口,它会自动生成映射逻辑。本文中,我们一起看下如何通过MapStruct处理集成关系。

我们将讨论三种方法:

  1. 通过实例检查;
  2. 使用访问者模式;
  3. 【推荐】使用@SubclassMapping注解。

一、背景

默认情况下,MapStruct无法为所有从基类或接口继承的类生成映射器,不支持运行时识别实例和对象层次结构。

(一)基础定义

先定义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;
}

再定义对应的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;
}

(二)定义映射器

这里我们定义三个映射器: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配置使用它们。

(三)识别问题

我们先看下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的实例对象,但是最终得到的只能是VehicleDTO的实例。

二、通过实例检查实现

我们一起看看如何通过实例检查实现映射逻辑,因为比较简单,直接代码:

@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;
        }
    }
}

从上面代码来说,其实完全是靠人工智能实现的,通过我们HardCode,借助instanceof实现类型判断,转换为需要的类型实例。

三、通过访问者模式实现

第二种方式是借助访问者模式实现,通过Java的多态,可以精准的调用到需要的方法。

(一)应用访问者模式

首先在抽象类Vehicle中定义抽象方法accept(),以接受任何访问者对象:

public abstract class Vehicle {
    public abstract VehicleDTO accept(Visitor visitor);
}

public interface Visitor {
    VehicleDTO visit(Car car);

    VehicleDTO visit(Bus bus);
}

现在,我们需要为每个Vehicle子类实现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);
    }
}

最后,我们可以通过实现访问者接口来实现映射器:

@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);
}

从性能来说,访问者模式方法比实例检查方法更优化,借助Java的多态实现,快速定位转换方法。从代码量来说,实例检查会由于访问者模式,因为写的少。

其实这里会引出一个问题,设计模式是好是坏?

四、使用@SubclassMapping实现

MapStruct提供了@SubclassMapping注解,允许我们配置的映射器,可以处理有继承关系的类实例转换。

@SubclassMapping注解中有source和target两个配置,source定义要映射的源子类,target定义要映射到的目标子类:

(一)定义

我们使用@SubclassMapping注解指定继承对应关系:

@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自动实现了实例检查,通过instanceof判断类型。

责任编辑:武晓燕 来源: 看山的小屋
相关推荐

2009-09-25 14:12:16

Hibernate继承

2020-11-01 17:10:46

异步事件开发前端

2025-01-24 00:00:00

MapStruct子类型Mapper

2012-07-17 09:16:16

SpringSSH

2024-07-08 09:03:31

2014-07-30 17:10:38

LVS集群负载均衡

2009-07-03 18:32:18

JSP页面跳转

2021-11-05 21:33:28

Redis数据高并发

2021-06-24 08:52:19

单点登录代码前端

2014-12-31 17:42:47

LBSAndroid地图

2019-11-20 18:52:24

物联网智能照明智能恒温器

2025-01-13 00:00:00

MapStruct枚举映射

2010-03-12 17:52:35

Python输入方式

2010-06-13 16:04:14

MySQL三种安装方式

2011-04-08 11:13:50

CISCO IOS令牌桶双桶

2025-01-16 00:00:00

MapStruct映射

2021-11-26 11:07:14

cowsay命令Linux

2010-08-24 09:43:33

2009-07-29 09:36:07

无线通信接入方式

2010-09-07 16:31:27

CSS
点赞
收藏

51CTO技术栈公众号