mapstruct应用

596 阅读3分钟

公众号:飞翔的代码

MapStruct这个库是用来做两个对象之间转换拷贝的,之前大家都使用BeanUtils工具类,在Spring和Apache common包里都有这个工具类,但是BeanUtils利用反射机制,在运行时反射再设置属性值效率很低,同时还不能够对类型不同的数据进行转换,所以我们采用MapStruct来实现

配置pom.xml

<properties>
    <mapstruct.version>1.4.1.Final</mapstruct.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${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>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>utf-8</encoding>

                <annotationProcessorPaths>
                    <!-- 如果使用lombok
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    -->
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>

            </configuration>
        </plugin>
    </plugins>
</build>

简单的一对一对应

先定义两个要转换的类

@Data
public class Data1 {
    private Long id;
    private String name;
    private Integer createTime;
}

@Data
public class Data2 {
    private String name;
    private Integer createTime;
}

创建一个转换接口

@Mapper
public interface TestConverter {
    TestConverter INSTANCE = Mappers.getMapper(TestConverter.class);

    /**
     * data1转data2
     * @param data1
     * @return
     */
    Data2 data1ToData2(Data1 data1);

    /**
     * data2转data1
     * @param data2
     * @return
     */
    Data1 data2ToData1(Data2 data2);
}

测试转换

Data1 data1 = new Data1();
data1.setId(1L);
data1.setName("hello world");
data1.setCreateTime(123);
System.out.println(TestConverter.INSTANCE.data1ToData2(data1));

注意:如果在idea中启动报没有实现类,或者修改了Data1和Data2但是转换后没有生效,需要重新build项目

自动类型映射

当Data1和Data2的属性类型不一致时,会自动进行类型转换,如下:

@Data
public class Data1 {
    private String createTime;
}

@Data
public class Data2 {
    private Integer createTime;
}

字段映射

需要手动指定映射时可以通过添加注解来实现,先将Data1稍做调整,使其和Data2属性名不一致

@Data
public class Data1 {
    private Long id;
    private String title;
    private String time;
}

在接口中添加注解,指明来源属性名和目标属性名

@Mapping(source = "title", target = "name")
@Mapping(source = "time", target = "createTime")
Data2 data1ToData2(Data1 data1);

自定义转换

再对Data1进行调整,添加prefix属性,期望转换时将prefix和title拼接起来赋值给Data2的name属性

@Data
public class Data1 {
    private Long id;
    private String prefix;
    private String title;
    private String time;
}

在接口中添加注解,将两个属性拼接起来

@Mapping(target = "name", expression = "java(data1.getPrefix() + data1.getTitle())")
Data2 data1ToData2(Data1 data1);

注意:使用expression就不能再指定target,另外对于复杂的转换,或者需要调用其他工具类,可以在接口中定义default方法来实现

@Mapping(target = "name", expression = "java(join(data1.getPrefix(), data1.getTitle()))")
Data2 data1ToData2(Data1 data1);

default String join(String str1, String str2) {
    return str1 + str2;
}

在上面的join方法内就可以写很多复杂的转换逻辑了

集合转换

只需要在接口中添加一个方法即可

List<Data2> data1ToData2List(List<Data1> data1List);

前提是接口中已经存在Data2 data1ToData2(Data1 data1);这个方法,原理是遇到集合转换时遍历集合调用data1ToData2方法,因此data1ToData2方法上添加的映射注解都有效

自定义抽象实现类

定义接口的抽象实现类来处理复杂的转换

在接口上添加@DecoratedWith注解指定抽象实现类

@Mapper
@DecoratedWith(AbstractTestConverter.class)
public interface TestConverter {
    TestConverter INSTANCE = Mappers.getMapper(TestConverter.class);

    /**
     * data1转data2
     * @param data1
     * @return
     */
    @Mapping(source = "time", target = "createTime")
    Data2 data1ToData2(Data1 data1);
}

定义抽象类

public abstract class AbstractTestConverter implements TestConverter {
    private final TestConverter delegate;

    public AbstractTestConverter(TestConverter delegate) {
        this.delegate = delegate;
    }

    @Override
    public Data2 data1ToData2(Data1 data1) {
        // 这里调用data1ToData2方法时接口中添加的@Mapping注解仍然有效
        Data2 data2 = delegate.data1ToData2(data1);
        data2.setName(data1.getPrefix() + data1.getTitle());
        return data2;
    }
}

公众号:飞翔的代码