Mapstruct使用说明(一)

        在使用分层或者分模块化的项目中,我们可能定义各种各样的O,例如:DO,VO,DTO等等。我们在进行这些对象之间的拷贝时,通过手动写get/set方法进行属性之间的赋值。因为他们之间的属性大部分都是相同的,不仅浪费时间,并且还有大量重复代码。所以,各种框架都添加的对象之间的拷贝的工具类。例如:

  1. Spring自带了BeanUtils

  2. Apatch自带的BeanUtils

  3. Apatch自带的PropertyUtils

  4. mapstruct提供Mappers

        本文主要介绍MapStruct的基础知识、Mapstruct的优缺点、Mapctruct的拷贝示例、以及四种方法时间对比。

01

Mapstuct介绍


1.mapstruct的官网地址:   

        https://mapstruct.org

2.mapstruct的文档地址:

        https://mapstruct.org/documentation/stable/reference/html/

3.mapstrcut的示例github地址

        https://github.com/mapstruct/mapstruct-examples

4.mapstruct的介绍

        mapstruct的作用:就像mapstruct官网所说的:mapsrtuct是一个用于简化在JavaBean之间映射的代码生成器工具,并且是基于约定高于配置的方式。

        因为生成的代码使用简单的get/set方法,所以mapstruct可以更快执行、类型之间复制更安全、且容易理解。

02

mapstruct的优缺点


下面看看mapstruct相比其他映射工具有什么优点

1.mapstruct的优点:

  • mapstruct是在代码编译的时候,生成其映射规则的代码,所以会在编译时暴露映射错误的代码,让错误提前暴露。

  • 因为使用的是一些简单方法:set/get或者Fluent方式,而非反射方式,所以可以更快的执行完成。可以通过04章节查看各个方法效率上对比

  • 可以实现深拷贝,上面4种方法中只有mapstruct可以实现深拷贝。但是需要进行配置(使用@mapper(mappingControl=DeepClone.class)进行设置)。其他的都是浅拷贝。从而mapstruct实现修改新对象不会对老对象产生影响。

  • 类型更加安全

  • 可以进行自定义的映射。可以自定义各种方法,完成属性映射。例如:将两个属性数据合并成一个设置到目标属性上

2.mapstruct的缺点

  • 必须添加一个接口或者抽象类,才能实现映射。其他的三种方法都不用写额外接口或者类。

03

Mapstruct的简单示例使用


        上面主要是介绍一下Mapstruct的基础知识,下面看一个mapstruct的简单示例,完成属性之间的映射。

        在示例中,我们使用mapstruct的1.4.2.Final版本,进行试验的

        1.第一个原属性数据

//第一个拷贝对象
public class CompareA {

    private String name;

    private Integer age;

    private LocalDateTime createTime;

    private double score;

    private Double totalScore;

    private Date updateTIme;

    private ChildCompare childCompare;

    private Long days;
    
    //省略其get/set方法,也可使用Lombok,以后讲解lombok与mapstruct结合使用
 }

       2. 第二个目标属性数据

public class CompareB {

    private String name;

    private Integer age;

    private String createTime;

    private double score;

    private Double totalScore;

    private Date updateTIme;

    private ChildCompare childCompare;

    private Long day;
    
    //省略其get/set方法,
}

       3.子类

//这是CompareA和CompareB共有的对象
public class ChildCompare {

    private String childName;

    private long childAge;
    
    //省略其get/set方法,
}

        上面是两个实体对象,观察这两个实体可以看到有两点不同:

  • CompareA中有一个createTime属性,数据类型为LocalDateTime,CompareB也有一个createTime,但是属性类型为String
  • CompareA有一个days属性,而CompareB有一个day属性

        4.mapstruct接口(这是需要我们手动写的)

package com.moxiao.properties.mapstruct;

import com.moxiao.properties.entity.CompareA;
import com.moxiao.properties.entity.CompareB;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.control.DeepClone;
import org.mapstruct.factory.Mappers;

/**
 * @author moxiao
 */
@Mapper
public interface CompareMapper {

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

    @Mapping(target = "day", source = "days")
    @Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(target = "childCompare", source = "childCompare", mappingControl = DeepClone.class)
    CompareB mapper(CompareA compareA);
}

        从上面的实现中,我们加入了三个注解:

  • 第一个注解:完成属性名不匹配的问题,将compareA中的days(source)赋值到compareB的day(target)
  • 第二个注解:完成属性类型不匹配的问题,将compareA中的createTime,赋值到compareB的createTime,这里进行日期的格式转化,使用dateFormat设置格式化类型
  • 第三个注解:完成深度拷贝,将compareA中的childCompare属性进行深度拷贝,而不是指针复制,最重要的是最后一个参数:mappingControl = DeepClone.class

        5.最后就会自动生成一个实现类:

package com.moxiao.properties.mapstruct;

import com.moxiao.properties.entity.ChildCompare;
import com.moxiao.properties.entity.CompareA;
import com.moxiao.properties.entity.CompareB;
import java.time.format.DateTimeFormatter;
import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor"
)
public class CompareMapperImpl implements CompareMapper {

    @Override
    public CompareB mapper(CompareA compareA) {
        if ( compareA == null ) {
            return null;
        }

        CompareB compareB = new CompareB();

        compareB.setDay( compareA.getDays() );
        if ( compareA.getCreateTime() != null ) {
            compareB.setCreateTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( compareA.getCreateTime() ) );
        }
        compareB.setChildCompare( childCompareToChildCompare( compareA.getChildCompare() ) );
        compareB.setName( compareA.getName() );
        compareB.setAge( compareA.getAge() );
        compareB.setScore( compareA.getScore() );
        compareB.setTotalScore( compareA.getTotalScore() );
        compareB.setUpdateTIme( compareA.getUpdateTIme() );

        return compareB;
    }

    protected ChildCompare childCompareToChildCompare(ChildCompare childCompare) {
        if ( childCompare == null ) {
            return null;
        }

        ChildCompare childCompare1 = new ChildCompare();

        childCompare1.setChildName( childCompare.getChildName() );
        childCompare1.setChildAge( childCompare.getChildAge() );

        return childCompare1;
    }
}

          从上面的实现,我们可以看出,添加一个childCompareToChildCompare方法,来完成对象之间的深度拷贝。同样的将days设置到了day属性,等等。

          6.最后,我们调用这个mapper方法,就可以完成对象之间的拷贝:如下:

CompareMapper.INSTANCE.mapper(new CompareA());

04

这个对象映射之间的比较


        我们在1,000,000个对象进行各个工具之间的对比,看看效率如何?

package com.moxiao.properties.mapstruct;

import com.moxiao.properties.entity.ChildCompare;
import com.moxiao.properties.entity.CompareA;
import com.moxiao.properties.entity.CompareB;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StopWatch;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author moxiao
 */
public class CompareTest {

    int count = 1000000;

    /**
     * Spring中的BeanUtils的使用,最后使用了6700ms
     */
    @Test
    public void springBeanUtils() {
        CompareA populate = populate();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("开始拷贝");
        for (int i = 0; i < count; i++) {
            CompareB compareB = new CompareB();
            BeanUtils.copyProperties(populate, compareB);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }

    /**
     * 这是使用mapstruct进行对象拷贝,使用时间54ms
     */
    @Test
    public void mapstructBeanUtils() {
        CompareA populate = populate();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("开始拷贝");
        for (int i = 0; i < count; i++) {
            CompareB mapper = CompareMapper.INSTANCE.mapper(populate);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }

    /**
     * 这是Apache的BeanUtils工具,使用7086ms
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    @Test
    public void commonBeanUtils() throws InvocationTargetException, IllegalAccessException {
        CompareA populate = populate();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("开始拷贝");
        for (int i = 0; i < count; i++) {
            CompareB compareB = new CompareB();
            org.apache.commons.beanutils.BeanUtils.copyProperties(compareB, populate);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }

    /**
     * 使用Apache中的propertyUtils静态方法,使用3756ms
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     */
    @Test
    public void commonPropertiesUtils() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        CompareA populate = populate();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("开始拷贝");
        for (int i = 0; i < count; i++) {
            CompareB compareB = new CompareB();
            PropertyUtils.copyProperties(compareB, populate);
        }
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
    }

    public CompareA populate() {
        CompareA compareA = new CompareA();
        compareA.setAge(10);
        compareA.setName("moxiao");
        compareA.setCreateTime(LocalDateTime.now());
        compareA.setDays(100L);
        compareA.setTotalScore(100.0);
        compareA.setScore(12.3);
        compareA.setUpdateTIme(new Date());
        ChildCompare childCompare = new ChildCompare();
        childCompare.setChildAge(200);
        childCompare.setChildName("moxiaoChild");
        compareA.setChildCompare(childCompare);
        return compareA;
    }

}

        pom文件如下:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.moxiao</groupId>
    <artifactId>propertiescopytest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.9</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.9</version>
        </dependency>
        
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.4.2.Final</version>
        </dependency>

        <!-- lombok dependencies should not end up on classpath -->
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</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>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>0.2.0</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>1.4.2.Final</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.18</version>
                        </path>
                    </annotationProcessorPaths>
                    <showWarnings>true</showWarnings>
                    <compilerArgs>
                        <arg>
                            -Amapstruct.suppressGeneratorTimestamp=true
                        </arg>
                        <arg>
                            -Amapstruct.suppressGeneratorVersionInfoComment=true
                        </arg>
                    </compilerArgs>

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

</project>

上面是各个工具之间的对比,1000000数据之间拷贝的时间为:

工具名称时间(ms)
mapstruct54
Spring的BeanUtils6700
Apache的common的BeanUtils7086
Apache的common的PropertyUtils3756

综上所述,1百万数据,mapstruct可以在50ms左右完成。

这篇文章只是Mapstruct文章的开头,后续还有:

  • 与Lombok结合使用以及注意事项。
  • 与SpringBoot进行DI
  • 对象之间的深度拷贝,需要最什么配置

更多精彩内容:请关注公众号:


 

### 回答1: MapStruct是一个Java的代码生成器,用于从一个对象映射到另一个对象。 要使用MapStruct创建转换,您需要定义源对象和目标对象之间的映射关系。这可以通过创建一个接口,并使用MapStruct注解来完成。 例如,假设您有一个源对象,称为“PersonDTO”,它有一个名称和年龄字段,以及一个目标对象,称为“PersonEntity”,它有一个名字和年龄字段。 您可以创建以下MapStruct转换接口: ``` @Mapper public interface PersonMapper { PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class); @Mapping(source = "name", target = "name") @Mapping(source = "age", target = "age") PersonEntity toEntity(PersonDTO personDTO); @Mapping(source = "name", target = "name") @Mapping(source = "age", target = "age") PersonDTO toDto(PersonEntity personEntity); } ``` 然后,您可以使用此接口将PersonDTO对象转换为PersonEntity对象,如下所示: ``` PersonDTO personDTO = new PersonDTO(); personDTO.setName("John Doe"); personDTO.setAge(30); PersonEntity personEntity = PersonMapper.INSTANCE.toEntity(personDTO); ``` 相反,您也可以使用该接口将PersonEntity对象转换为PersonDTO对象: ``` PersonEntity personEntity = new PersonEntity(); personEntity.setName("John Doe"); personEntity.setAge(30); PersonDTO personDTO = PersonMapper.INSTANCE.toDto(personEntity); ``` 这就是如何使用MapStruct进行对象转换的简单示例。 ### 回答2: MapStruct 是一个用于 Java 对象之间映射转换的代码生成器。下面我将举一个简单的例子来说明如何使用 MapStruct 进行转换。 假设我们有两个类:Person 和 PersonDTO。Person 类包含了人的基本信息,如姓名、年龄和性别,而 PersonDTO 类则是 Person 类的数据传输对象,它只包含姓名和年龄两个属性。 首先,我们需要在 pom.xml 文件中添加 MapStruct 依赖: ``` <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.4.2.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.4.2.Final</version> <scope>provided</scope> </dependency> ``` 接下来,我们需要创建一个转换器接口,该接口使用MapStruct 的注解来定义转换规则: ```java @Mapper public interface PersonMapper { PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class); @Mapping(source = "name", target = "name") @Mapping(source = "age", target = "age") PersonDTO personToDto(Person person); } ``` 在上述代码中,@Mapper 注解表示这是一个 MapStruct 的转换器接口,通过 @Mapping 注解来指定属性的映射关系。 最后,在使用转换器进行对象转换的地方调用相应的方法即可: ```java Person person = new Person(); person.setName("张三"); person.setAge(25); PersonDTO personDTO = PersonMapper.INSTANCE.personToDto(person); System.out.println(personDTO.getName()); System.out.println(personDTO.getAge()); ``` 上述代码中,我们先创建一个 Person 对象,然后使用转换器的 personToDto 方法将其转换为 PersonDTO 对象,最后打印出 PersonDTO 对象的属性值。 这就是使用 MapStruct 进行对象之间映射转换的简单例子。当然,MapStruct 还提供了更多高级的功能和配置选项,可以根据具体需求进行深入学习和使用。 ### 回答3: MapStruct是一个开源的Java注解处理器,用于从一个Java对象转换为另一个Java对象。它通过通过使用注解来定义转换规则和生成转换代码。 以下是一个简单的MapStruct转换示例: 1. 首先,定义需要转换的源对象和目标对象的类。例如,我们有一个名为`Person`的源对象类和一个名为`PersonDto`的目标对象类。 ```java public class Person { private String name; private int age; // getters and setters } public class PersonDto { private String fullName; private int age; // getters and setters } ``` 2. 接下来,使用MapStruct的注解在源对象和目标对象之间创建映射规则。创建一个名为`PersonMapper`的接口,并使用`@Mapper`注解来指定映射规则。 ```java @Mapper public interface PersonMapper { PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class); @Mapping(source = "name", target = "fullName") PersonDto personToPersonDto(Person person); } ``` 3. 最后,通过调用生成的映射方法将源对象转换为目标对象。 ```java Person person = new Person(); person.setName("John"); person.setAge(30); PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person); ``` 在上面的示例中,`personToPersonDto`方法将从`Person`对象的`name`属性映射到`PersonDto`对象的`fullName`属性,并将`age`属性直接复制到目标对象中。 这就是一个简单的MapStruct转换示例。MapStruct提供了更丰富的功能,例如支持集合、自定义转换逻辑等。通过编写合适的映射规则和使用MapStruct的注解,您可以轻松地完成对象之间的转换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值