MapStruct-实体映射处理器

MapStruct-实体映射处理器


对象映射大体分为两种:

  • 运行期:反射调用set/get 或者是直接对成员变量赋值 。 * 该方式通过invoke执行赋值,实现时一般会采用beanutil, Javassist等开源库。这类的代表:Dozer,ModelMapper
  • 编译期:动态生成set/get代码的class文件 ,在运行时直接调用该class文件。* 该方式实际上仍会存在set/get代码,只是不需要自己写了。 这类的代表:MapStruct,Selma,Orika

MapStruct简化Bean对象之间的映射处理,其实质就是set/get的自动处生成转换理,参考官网:

  • MapStruct

导入依赖包

    compile('org.mapstruct:mapstruct-jdk8:1.2.0.Final')
    annotationProcessor("org.mapstruct:mapstruct-processor:1.2.0.Final")

如果引入以上包后编译无法生效,需要导入以下配置:

    plugins {
        id 'net.ltgt.apt' version '0.15'
    }
    apply plugin: 'net.ltgt.apt-idea'
    apply plugin: 'net.ltgt.apt-eclipse'
    /**
     * 配置mapstruct任务
     * http://mapstruct.org/documentation/dev/reference/html/#_gradle
     */
    tasks.withType(JavaCompile) {
        options.compilerArgs = [
                '-Amapstruct.suppressGeneratorTimestamp=true',
                //默认交由spring管理,在外部可以直接使用@Autowired注入mapper
                '-Amapstruct.defaultComponentModel=spring',
                '-Amapstruct.suppressGeneratorVersionInfoComment=true',
                '-Amapstruct.unmappedTargetPolicy=IGNORE'
        ]
    }

1. PersonDto

定义数据源对象

public class PersonDto {
    private String nameDto;
    private int age;
    private String address;
    private String idno;
    private double money;
    private Date birthDate;
}

2. Person

定义目标对象

public class Person {
    private String name;
    private int age;
    private String address;
    private String idno;
    private double money;
    private Date birthDate;
}

注意:其中name属性和PersonDto的nameDto不是同名

3. PersonMapStruct

定义源、目标对象映射关联接口

@Mapper(componentModel = "spring")
public interface PersonMapStruct {

    PersonMapStruct INSTANCE = Mappers.getMapper(PersonMapStruct.class);

    @Mappings({
            @Mapping(source = "nameDto", target = "name"),
//            @Mapping(target = "birthDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(personDto.getBirthDate(),\"yyyy-MM-dd\"))")
    })
    Person dtoToPoByPersonDto(PersonDto personDto);
}

注意

  • 如果属性名称完全相同则不做处理,相反不同属性名称之间的映射可以使用@Mapping(source = “nameDto”, target = “name”)

  • 可以使用一些高级特性,如expression属性可以定义java代码用以进行特殊处理

  • componentModel属性用于指定自动生成的接口实现类的组件类型

    这个属性支持四个值:
    default: 这是默认的情况,mapstruct不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
    cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
    spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
    jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取。
    

4. PersonMapStructImpl

编译完成后会生成接口的实现类

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-01-28T17:18:38+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_131 (Oracle Corporation)"
)
@Component
public class PersonMapStructImpl implements PersonMapStruct {

    @Override
    public Person dtoToPoByPersonDto(PersonDto personDto) {
        if ( personDto == null ) {
            return null;
        }

        Person person = new Person();

        person.setName( personDto.getNameDto() );
        person.setAge( personDto.getAge() );
        person.setAddress( personDto.getAddress() );
        person.setIdno( personDto.getIdno() );
        person.setMoney( personDto.getMoney() );
        person.setBirthDate( personDto.getBirthDate() );

        return person;
    }
}

注意:根据生成在target包下的源码可看出, mapstruct实则是生成了一些set/get方法,且对一些数据进行转换处理等

5. 使用

Person person = PersonMapStruct.INSTANCE.dtoToPoByPersonDto(personDto);

6. 性能测试

针对几种常见的Bean复制方式,分别进行简单的性能测试得出(单位:ms):
MapStruct-实体映射处理器_第1张图片

10次测验 第一次 第二次 第三次 平均值 每次平均值
BeanUtil.copyProperties 131 130 137 132.667 13.2667
PropertyUtils.copyProperties 78 104 87 89.667 8.9667
spring.BeanUtils.copyProperties 1 1 1 1 0.1
BeanCopier.create 0 0 1 0.3333 0.0333
MapStruct 3 3 3 3 0.3
10000次测验 第一次 第二次 第三次 平均值 每次平均值
BeanUtil.copyProperties 235 231 203 223 22.3
PropertyUtils.copyProperties 721 662 625 429.3092 42.9309
spring.BeanUtils.copyProperties 11 11 11 11 0.11
BeanCopier.create 2 2 3 2.3333 0.2333
MapStruct 5 5 9 6.3333 0.6333
100000次测验 第一次 第二次 第三次 平均值 每次平均值
BeanUtil.copyProperties 500 570 572 547.3333 54.7333
PropertyUtils.copyProperties 3572 2475 3796 3281 328.1
spring.BeanUtils.copyProperties 58 55 51 54.6667 5.4667
BeanCopier.create 4 3 4 3.6667 0.3667
MapStruct 10 11 9 10 1

:内存12G、四核i7-4720QU处理器

根据测试结果得出,Cglib在测试中的优势较为明显,这得益于它的缓存机制,大家可以看下java.lang.reflect.WeakCache类,不过即便没有缓存,性能依旧是不错,几乎零消耗;PropertyUtils的结果随着调用增长,呈迅速增长趋势,相对spring的copy则是稳步增长,最后是MapStruct则相对平稳且相对来说很稳健,Cglib基于类反射代理,只拷贝名称和类型都相同的属性,所以当目标类的setter数目比getter少时,创建BeanCopier会失败而导致拷贝不成功,需要手动去处理

综上所述,相对来说,实现Bean拷贝的自动化处理,且侵入较小,性能也保证的前提下,Cglib和MapStruct都是不错的选择,不过对于更好的自定义和可读性来说,推荐MapStruct。

你可能感兴趣的:(JAVA)