MapStructMapStruct 是一个代码生成器,它基于约定优于配置方法极大地简化了 Java bean 类型之间映射的实现。生成的映射代码使用简单的方法调用,因此速度快、类型安全且易于理解。MapStruct 是一个注解处理器,它插入到 Java 编译器中,可用于命令行构建(Maven、Gradle 等)或者IDE。MapStruct 使用合理的默认值,但在配置或实现特殊行为时会避开自定义的实现方式。MapStruct Spring Extensions 已经加入到org.springframework.core.convert包中作为一种转换的实现提供使用
在分布式架构(或微服务架构)需要拆分模块时,不得不思考一个问题:WEB 层能不能出现 DAO 或者 DO 对象?我觉得不行。服务层和表现层应当解耦,后者不应当触碰到任何持久化对象,其所有的数据来源,均应当由前者提供 ,而这需要在不同的对象模型(例如实体和 DTO)之间进行映射。编写这样的映射代码是一项乏味且容易出错的任务。MapStruct 旨在通过尽可能自动化来简化这项工作。映射代码的工具有很多种,如各种BeanUtils等,与其他映射框架相比,MapStruct 在编译时生成 bean 映射,以确保高性能和安全.
数据流对比 可以看 5种常见Bean映射工具的性能比对 (juejin.cn)
市面上还是有很多的相关代码映射工具,如
ModelMapper(GitHub - modelmapper/modelmapper: Intelligent object mapping)BeanUtilsselma(GitHub - xebia-france/selma: Selma Java bean mapping that compiles)mapstruct以上工具可以大概分为2类
set/get或者直接对成员变量赋值, 一般都是调用反射包的invoke 方法 **
BeanUtils 都是通过java.beans.PropertyDescriptor和reflect包来进行对应的处理,apache 支持名称相同但类型不同的属性的转换,spring 支持忽略某些属性不进行映射ModelMapper 也是在reflect包封装反射支持的selma 使用静态编译生成字节码,而不会在运行时或在字符串字段中编写的伪代码进行任何反射。mapStruct 是最初提出了映射生成的想法,功能更加丰富,社区建设比较好从性能、问题排查、文档、成熟度、扩展性等因素来考虑,MapStruct是一个不错的选择;
pom加载依赖
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>
此外,需要加载maven的compiler 插件
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>15</source>
                <target>15</target>
                <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>
                <compilerArgs>--enable-preview</compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>
编写对应的转换实体和转换Mapper
CarEntity
@Data
public class CarDto{
    private String make;
    private Integer seatCount;
    private String type;
}
CarDTO
@Data
public class CarDto{
    private String make;
    private Integer seatCount;
    private String type;
}
CarMapper
CarMapper 有多种 实现方式,建议如果要使用Mapper的话,继承org.springframework.core.convert.converter.Converter,2个类的成员变量基本相同的情况下,可以不用做额外的方法处理,Mapper最常见的还是以下2种
SpringBean2种方式都行,代码如下
@Mapper
//@Mapper(componentModel = "spring") //第一种方式
public interface CarMapper extends Converter<Car, CarDto> {
	//第二种方式
    CarMapper MAPPER = Mappers.getMapper(CarMapper.class);
    @Mapping(target = "seatCount", source = "numberOfSeat")
    @Override
    CarDto convert(Car car);
}
test
测试类采用的第二种方式进行的转换,可见在使用方面还是比较方便的
@Test
public void transferTest(){
    Car car = new Car();
    car.setMake("转换测试");
    car.setNumberOfSeat(11);
    car.setType("dd");
    System.out.println(car);
    CarDto convert = CarMapper.MAPPER.convert(car);
    System.out.println(convert);
}
下面选择几个常用场景描述下
@Mapper
public interface AddressMapper {
    @Mapping(source = "person.description", target = "description")
    @Mapping(source = "address.houseNo", target = "houseNumber")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
@Mapper(componentModel = "spring")
public interface CarMapper {
    CarDto carToCarDto(Car car);
}
3.调用其他的映射
@Mapper(uses=DateMapper.class)
public class CarMapper {
    CarDto carToCarDto(Car car);
}
@Repository // CDI component model
public class ReferenceMapper {
    @PersistenceContext
    private EntityManager entityManager;
    public <T extends BaseEntity> T resolve(Reference reference, @TargetType Class<T> entityClass) {
        return reference != null ? entityManager.find( entityClass, reference.getPk() ) : null;
    }
    public Reference toReference(BaseEntity entity) {
        return entity != null ? new Reference( entity.getPk() ) : null;
    }
}
@Mapper(componentModel = "cdi", uses = ReferenceMapper.class )
public interface CarMapper {
    Car carDtoToCar(CarDto carDto);
}
@Mapper( uses = Titles.class )
public interface MovieMapper {
     @Mapping( target = "title", qualifiedByName = { "TitleTranslator", "EnglishToGerman" } )
     GermanRelease toGerman( OriginalRelease movies );
}
@Named("TitleTranslator")
public class Titles {
    @Named("EnglishToGerman")
    public String translateTitleEG(String title) {
        // some mapping logic
    }
    @Named("GermanToEnglish")
    public String translateTitleGE(String title) {
        // some mapping logic
    }
}
mapStruct  采用了JDK6中的新特性 插入式注解处理API(Pluggable Annotation Processing API) ,lombok注解,IDEA在编写代码时候的标记语法错误的红色下划线都是通过这个特性实现的.其主要抽象类是AbstractProcessor,需要注意的是,该API只处理编译期注解
插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175),实际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements, 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境. JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列. JSR 269主要被设计成为针对Tools或者容器的API.
定义annotation process:  org.mapstruct.ap.MappingProcessor,并继承javax.annotation.processing.AbstractProcessor
定义注解 org.mapstruct.Mapper,并将运行策略改成@Retention(RetentionPolicy.SOURCE)
在MappingProcessor中使用javax.annotation.processing.SupportedAnnotationTypes指定在第2步创建的注解类型的名称(注意需要全类名,"包名.注解类型名称",否则会不生效)
在MappingProcessor中使用javax.annotation.processing.SupportedSourceVersion指定编译版本SourceVersion.latestSupported()。
在MappingProcessor中使用javax.annotation.processing.SupportedOptions指定编译参数。
指定processor参与编译
直接使用编译参数指定,javac -processor org.mapstruct.ap.MappingProcessor Main.java。
通过服务注册指定,就是META-INF/services/javax.annotation.processing.Processor文件中添加org.mapstruct.ap.MappingProcessor。
通过Maven的编译插件的配置指定如下:
		<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
                <annotationProcessors>
                    <annotationProcessor>
                         <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                    	</path>
                    </annotationProcessor>
                </annotationProcessors>
            </configuration>
        </plugin>

mapStruct虽然说实现的功能流程简单, 但是它巧妙利用了Types和Elements,将复杂的class生成分析转成对应去处理,倒是有其独特和称赞的地方
原文:https://www.cnblogs.com/wzqshb/p/14907761.html