MapStruct是一个对象属性复制工具,个别作用于不同的分层模型的对象属性复制。
从网上copy了下他人测试的性能比照
pc配置:i7,16G内存
各种Bean拷贝工具比拟
工具 | 十个对象复制1次 | 一万个对象复制1次 | 一百万个对象复制1次 | 一百万个对象复制5次 |
---|---|---|---|---|
mapStruct | 0ms | 3ms | 96ms | 281ms |
hutools的BeanUtil | 23ms | 102ms | 1734ms | 8316ms |
spring的BeanUtils | 2ms | 47ms | 726ms | 3676ms |
apache的BeanUtils | 20ms | 156ms | 10658ms | 52355ms |
apache的PropertyUtils | 5ms | 68ms | 6767ms | 30694ms |
起源:MapStruct应用及性能测试,秒杀BeanUtil
根底应用
依赖配置
pom.xml配置以下内容,例子中应用了lombok,所以我把lombok的配置也加上了
<project> <properties> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version> <lombok.version>1.18.20</lombok.version> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> <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> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>
官网例子
@Datapublic class Car { private String make; private int numberOfSeats; private CarType type;}@Datapublic class CarDto { private String make; private int seatCount; private String type; }@Mapperpublic interface CarMapper { CarMapper INSTANCE = Mappers.getMapper(CarMapper.class); // 字段名不同时,能够应用@Mapping配置关系 @Mapping(source = "numberOfSeats", target = "seatCount") CarDto carToCarDto(Car car);}@Testpublic void shouldMapCarToDto() { //given Car car = new Car("Morris", 5, CarType.SEDAN); //when CarDto carDto = CarMapper.INSTANCE.carToCarDto(car); //then assertThat(carDto).isNotNull(); assertThat(carDto.getMake()).isEqualTo( "Morris"); assertThat(carDto.getSeatCount()).isEqualTo(5); assertThat(carDto.getType()).isEqualTo("SEDAN");}
封装
从下面的例子中,每次应用都须要调用一次Mapper.INSTANCE
能力获取到Mapper,这样Mapper就会和业务代码耦合在一起,不利于当前替换其余工具。咱们能够把对象属性复制的性能形象成一个接口Convert
,所有Bean都是Convert
的子类,这样每个Bean都有对象转换的能力。
public interface Convert extends Serializable { /** * 获取主动转换后的JavaBean对象 * * @param clazz class类型 * @param <T> 类型 * @return 对象 */ @SuppressWarnings({"unchecked", "rawtypes"}) default <T> T convert(Class<T> clazz) { BeanConvertMapper mapper = BeanConvertMappers.getMapper(this.getClass(), clazz); return (T) mapper.to(this); }}
BeanConvertMapper
定义了一个对象转换的接口
public interface BeanConvertMapper<SOURCE, TARGET> { /** * source to target * * @param source source * @return target */ TARGET to(SOURCE source);}
BeanConvertMappers
是一个工具类,提供通过源对象Class
和指标对象Class
获取Mapper办法。
@SuppressWarnings({"rawtypes", "unchecked"})public class BeanConvertMappers { public static <S, T> BeanConvertMapper<S, T> getMapper(Class<S> sourceClass, Class<T> targetClass) { String key = MapperDefinition.generateKey(sourceClass, targetClass); Class mapperClass = MapperDefinition.getMappers().get(key); if (mapperClass == null) { throw new IllegalArgumentException(StrUtil.format("找不到{}转{}的Mapper", sourceClass.getName(), targetClass.getName())); } return (BeanConvertMapper<S, T>) Mappers.getMapper(mapperClass); }}
MapperDefinition
保护所有Mapper
,新增Mapper
只须要注册到map即可。
@SuppressWarnings("rawtypes")public class MapperDefinition { private static Map<String, Class> MAPPERS = new HashMap<>(16); static { registerMapper(CarDto.class, Car.class, CarDtoToCarMapper.class); // 新增的Mapper在这注册 MAPPERS = MapUtil.unmodifiable(MAPPERS); } /* Mapper定义 */ @Mapper public interface CarDtoToCarMapper extends BeanConvertMapper<LabelingReq, LabelingBO> { } /* Mapper定义 */ public static Map<String, Class> getMappers() { return MAPPERS; } public static <S, T> String generateKey(Class<S> sourceClass, Class<T> targetClass) { return sourceClass.getName() + targetClass.getName(); } private static <S, T> void registerMapper(Class<S> sourceClass, Class<T> targetClass, Class<? extends BeanConvertMapper<S, T>> mapperClass) { MAPPERS.put(generateKey(sourceClass, targetClass), mapperClass); }}
进一步优化
下面的封装解决了Mapper耦合的问题,然而在定义Mapper的时候,还是存在大量的模板接口,是否有更好的形式解决呢?
我想到的计划是:
和mapstruct原理一样,在mapstruct的注解处理器之前,通过注解来生成BeanConvertMapper
接口,注解大抵如下,同时主动注入到map中。新增一个Mapper只须要定义一个注解即可。
@Target(ElementType.TYPE)@Retention(RetentionPolicy.CLASS)public @interface MapperDefinition { /** * 源对象的Class * * @return Class */ Class<?> source(); /** * 指标对象的Class * * @return Class */ Class<?> target();}
你有更好的计划吗,一起分享下