MapStruct实现对象映射

1 序

mapstruct.png

MapStruct是一个属性映射工具,只需要使用@Mapper注解标注的映射接口。MapStruct就会自动生成实现这个映射接口的实现类,避免了复杂繁琐的映射实现。MapStruct官网地址: http://mapstruct.org/

2 准备工作

2.1 定义映射示例Bean

  • 定义Mode Bean
package com.eugeneheen.mapstruct.api.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;

@Data
public class UserModel {
    private Long id;

    private String name;

    private String phone;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime createTm;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    LocalDateTime updateTm;
}
  • 定义Do Bean
package com.eugeneheen.mapstruct.dao.dataobject;

import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity(name = "t_user")
public class UserDo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String name;

    @Column
    private String phone;

    @Column(name = "create_tm")
    LocalDateTime createTm;

    @Column(name = "update_tm")
    LocalDateTime updateTm;
}

2.2 示例使用的相关依赖

  • Lombok
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <optional>true</optional>
</dependency>
  • 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>
</dependency>
<dependency>
    <groupId>org.mapstruct.extensions.spring</groupId>
    <artifactId>mapstruct-spring-annotations</artifactId>
    <version>0.1.0</version>
</dependency>
  • MapStruct第三方依赖
<dependency>
     <groupId>io.github.zhaord</groupId>
     <artifactId>mapstruct-spring-plus-boot-starter</artifactId>
     <version>1.0.1.RELEASE</version>
</dependency>

3 示例

3.1 关于依赖

3.1.1 Maven

  • SpringBoot单模块项目:直接在POM文件加入相关依赖即可。
  • SpringBoot多模块项目
    • 打包的模块,POM文件加入以下依赖:
    <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>
              </configuration>
          </plugin>
          <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
              <version>2.4.1</version>
              <configuration>
                  <mainClass>com.eugeneheen.mapstruct.MapStructApplication</mainClass>
                  <skip>false</skip>
                  <layers>
                  <enabled>true</enabled>
                  </layers>
                  <excludes>
                  <!-- 打包的时候忽略lombok,解决生成了maptruct的实现类,但该类只创建了对象,没有进行赋值 -->
                  <exclude>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                  </exclude>
                  </excludes>
              </configuration>
              <executions>
                  <execution>
                  <id>repackage</id>
                  <goals>
                      <goal>repackage</goal>
                  </goals>
                  </execution>
              </executions>
          </plugin>
      </plugins>
    </build>
    
    • 定义映射接口的模块,POM文件加入以下依赖:
      <dependencies>
          <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>
          </dependency>
          <dependency>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
              <version>1.18.22</version>
              <optional>true</optional>
          </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依赖一定要放在,Mapstruct-processor依赖之前。否则,生成了maptruct的实现类,但该类只创建了对象,没有进行赋值 -->
                          <path>
                              <groupId>org.projectlombok</groupId>
                              <artifactId>lombok</artifactId>
                              <version>1.18.22</version>
                          </path>
                          <path>
                              <groupId>org.mapstruct</groupId>
                              <artifactId>mapstruct-processor</artifactId>
                              <version>1.4.2.Final</version>
                          </path>
                      </annotationProcessorPaths>
                  </configuration>
              </plugin>
          </plugins>
      </build>
      

3.1.2 Gradle

  • version >= 4.6
dependencies {
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
  • version < 4.6
plugins {
    id 'net.ltgt.apt' version '0.21'
}
dependencies {
    compile 'org.mapstruct:mapstruct:1.4.2.Final'
    apt 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

3.2 基础使用示例

  • 定义映射接口
package com.eugeneheen.mapstruct.api.struct;

import com.yongyou.eshs.api.model.UserModel;
import com.yongyou.eshs.dao.dataobject.UserDo;
import org.mapstruct.Mapper;

@Mapper
public interface IUserStruct {
    IUserStruct INSTANCE = Mappers.getMapper( IUserStruct.class );
    
    UserDo modelToDo(UserModel userModel);
    UserModel doToModel(UserDo user);
}
  • Service中使用
public interface IUserService {
    UserModel findById(Long id);
}

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserRepository userRepository;

    @Override
    public UserModel findById(Long id) {
        UserDo user = this.userRepository.getOne(id);
        // 此处,由于映射定义未使用Spring注入,所以使用常量获实例再调用映射方法
        return IUserStruct.INSTANCE.toModel(user);
    }
}

3.3 注入到Spring的使用示例

  • 定义映射接口
  package com.eugeneheen.mapstruct.api.struct;
  
  import com.yongyou.eshs.api.model.UserModel;
  import com.yongyou.eshs.dao.dataobject.UserDo;
  import org.mapstruct.Mapper;
  
   // 使用@Mapper注解的componentModel属性值制定为Spring,实现Spring注入
  @Mapper(componentModel = "spring")
  public interface IUserStruct {
      UserDo modelToDo(UserModel userModel);
      UserModel doToModel(UserDo user);
  }
  • Service中使用
public interface IUserService {
    UserModel findById(Long id);
}

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserRepository userRepository;

    // 此处,注入由Spring管理的映射实现
    @Autowired
    private IUserStruct userStruct;

    @Override
    public UserModel findById(Long id) {
        UserDo user = this.userRepository.getOne(id);
        return this.userStruct.toModel(user);
    }
}

3.4 使用Spring扩展依赖的使用示例

待续......

4 常见问题

4.1 Maven构建出现生成了MapStruct的实现类,但该类只创建了对象,没有进行赋值的问题

  • 定义映射接口的模块/工程,maven-compiler-plugin插件,配置lombok-mapstruct-binding依赖,且依赖版本由0.1.0升级到0.2.0
<dependencies>
    <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>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
        <optional>true</optional>
    </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依赖一定要放在,Mapstruct-processor依赖之前。否则,生成了maptruct的实现类,但该类只创建了对象,没有进行赋值 -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.22</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <!-- 如果是0.1.0 有可能出现生成了MapStruct的实现类,但该类只创建了对象,没有进行赋值 -->
                        <version>0.2.0</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.4.2.Final</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
  • 多项目打包时,在父项目/打包模块POM中配置spring-boot-maven-plugin插件时,排除``依赖。详细配置见3.1.1章节,SpringBoot多模块项目配置

5 MapStruct与各种BeanUtil性能比较

工具 十个对象复制1次 一万个对象复制1次 一百万个对象复制1次 一百万个对象复制5次
MapStruct 0ms 3ms 96ms 281ms
Hutool的BeanUtil 23ms 102ms 1734ms 8316ms
Spring的BeanUtils 2ms 47ms 726ms 3676ms
Apache的BeanUtils 20ms 156ms 10658ms 52355ms

测试数据仅供参考,根据不同主机配置均有不同,单此处数据也能客观表现性能

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容