MapStruct - 一篇就能上手 MapStruct_mapstruct中 使用 service-程序员宅基地

技术标签: Commons copy  BeanUtils Orika  # JavaWeb  MapStruct  SpringBoot  Cglib Dozer  

简介

MapStruct是满足JSR269规范的一个Java注解处理器,用于为Java Bean生成类型安全且高性能的映射。它基于编译阶段生成get/set代码,此实现过程中没有反射,不会造成额外的性能损失。

您所要做的就是定义一个mapper接口(@Mapper),该接口用于声明所有必须的映射方法。在编译期间MapStruct会为该接口自动生成实现类。该实现类使用简单的Java方法调用来映射source-target对象,在此过程中没有反射或类似的行为发生。

性能

优点

与手工编写映射代码相比

  • MapStruct通过生成冗长且容易出错的代码来节省时间。

与动态映射框架相比

  • 简单泛型智能转换;
  • 效率高:无需手动 set/get 或 implements Serializable 以达到深拷贝;
  • 性能更高:使用简单的Java方法调用代替反射;
  • 编译时类型安全:只能映射相同名称或带映射标记的属性;
  • 编译时产生错误报告:如果映射不完整(存在未被映射的目标属性)或映射不正确(找不到合适的映射方法或类型转换)则会在编译时抛出异常。

使用

1、引入 Pom

方法一

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.2.0.Final</version>
</dependency>

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.2.0.Final</version>
</dependency>

方法二

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.0.Final</version>
</dependency>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <source>8</source>
        <target>8</target>
        <encoding>UTF-8</encoding>
        <annotationProcessorPaths>
            <!-- 必须要加, 否则生成不了 MapperImpl 实现类 -->
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.4.0.Final</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

2、注入方式

  • Spring 注入方式
/**
 * @author Lux Sun
 * @date 2021/2/22
 */
@Mapper(componentModel = "spring")
public interface ApiMapper {
}
  • ClassLoader 加载方式
/**
 * @author Lux Sun
 * @date 2021/2/22
 */
@Mapper
public interface ApiMapper {
    ApiMapper INSTANCE = Mappers.getMapper(ApiMapper.class);
}

3、案例(ApiVO 转 ApiDTO)

ApiMapper

/**
 * @author Lux Sun
 * @date 2021/2/22
 */
@Mapper(componentModel = "spring")
public interface ApiMapper {
    // ClassLoader 加载方式
    ApiMapper INSTANCE = Mappers.getMapper(ApiMapper.class);

    /**
     * ApiVO 转 ApiDTO
     * @param apiVO
     */
    @Mapping(source = "method", target = "apiMethod")
    ApiDTO apiVoToApiDto(ApiVO apiVO);
}

ApiMapperTest

@Resource
private ApiMapper apiMapper;

/**
 * ApiVO 转 ApiDTO
 * @param
 * @return void
 */
public void apiVoToApiDtoTest() {
    // 创建 ApiPOList
    ApiPO apiPO1 = ApiPO.builder().name("apiName1").build();
    List<ApiPO> apiPOList = Lists.newArrayList();
    apiPOList.add(apiPO1);
    
    // 创建 ApiConf
    ApiVO.ApiConf apiConf = ApiVO.ApiConf.builder().id("123456").build();
    
    // 创建数字 List
    List<Integer> numberList = Lists.newArrayList();
    numberList.add(1);
    numberList.add(2);
    
    // 创建 ApiVO
    ApiVO apiVO = ApiVO.builder().name("apiName").method(1)
                                .apiPOList(apiPOList).apiConf(apiConf)
                                .list(numberList).build();
    
    // 转换结果 ApiDTO
    // ClassLoader 调用方式
    ApiDTO apiDTO = ApiMapper.INSTANCE.apiVoToApiDto(apiVO);
    // Spring 调用方式
    // ApiDTO apiDTO = apiMapper.apiVoToApiDto(apiVO);
    System.out.println(apiDTO);
}

结果输出

ApiDTO(name=apiName, apiMethod=1, apiConf=ApiDTO.ApiConf(id=123456, open=null), apiPOList=[ApiPO(name=apiName1, method=null, apiConf=null, apiPOList=null)], list=[1, 2])

原理

通过 JSR 269 Java注解处理器自动生成该文件

ApiMapperImpl

package com.luxsun.platform.lux.kernel.common.convert;
import com.luxsun.platform.lux.kernel.common.domain.dto.ApiDTO;
import com.luxsun.platform.lux.kernel.common.domain.dto.ApiDTO.ApiConf;
import com.luxsun.platform.lux.kernel.common.domain.po.ApiPO;
import com.luxsun.platform.lux.kernel.common.domain.vo.ApiVO;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-02-22T14:48:03+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_121 (Oracle Corporation)"
)
@Component
public class ApiMapperImpl implements ApiMapper {
    @Override
    public ApiDTO apiVoToApiDto(ApiVO apiVO) {
        if ( apiVO == null ) {
            return null;
        }
        ApiDTO apiDTO = new ApiDTO();
        apiDTO.setApiMethod( apiVO.getMethod() );
        apiDTO.setName( apiVO.getName() );
        apiDTO.setApiConf( apiConfToApiConf( apiVO.getApiConf() ) );
        List<ApiPO> list = apiVO.getApiPOList();
        if ( list != null ) {
            apiDTO.setApiPOList( new ArrayList<ApiPO>( list ) );
        }
        else {
            apiDTO.setApiPOList( null );
        }
        apiDTO.setList( integerListToStringList( apiVO.getList() ) );
        return apiDTO;
    }
    protected ApiConf apiConfToApiConf(com.luxsun.platform.lux.kernel.common.domain.vo.ApiVO.ApiConf apiConf) {
        if ( apiConf == null ) {
            return null;
        }
        ApiConf apiConf1 = new ApiConf();
        apiConf1.setId( apiConf.getId() );
        apiConf1.setOpen( apiConf.getOpen() );
        return apiConf1;
    }
    protected List<String> integerListToStringList(List<Integer> list) {
        if ( list == null ) {
            return null;
        }
        List<String> list1 = new ArrayList<String>( list.size() );
        for ( Integer integer : list ) {
            list1.add( String.valueOf( integer ) );
        }
        return list1;
    }
}

Ps:这就是为什么 mapstruct 的效率比较高的原因,相比于反射获取对象进行拷贝的方法,这种更贴近于原生 get/set 方法的框架显得更为高效。这个文件是通过在 mapper 中的注解,使用生成映射器的注解处理器从而自动生成了这段代码。

注意事项

  • 开发过程中遇到修改属性时,重新启动项目还是复制的时候新添加的属性为 null,此时,因为自动生成的实现类并没有重新编译,需要手动在 target 包中找到删除,再重新启动即可!

FAQ

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Dream_Weave/article/details/113946112

智能推荐

Android10.0-多用户切换流程源码分析_android 访客模式源码-程序员宅基地

文章浏览阅读3.2k次。多用户切换流程代码路径:packages/apps/Settings/src/com/android/settings/users/UserSettings.java切换新用户流程:1.多用户下点击事件public boolean onPreferenceClick(Preference pref)2.这段代码机主用户下切换新用户:else if (pref instanceof UserPrefe..._android 访客模式源码

(上部)你要的 wechaty 微信机器人教程_wechaty使用教程-程序员宅基地

文章浏览阅读7.8k次,点赞6次,收藏21次。“wechaty | 微信社群管理 - 机器人”Hello,大家好。我是公众号“八点半技术站”的小编-Bruce.D。今天是周三(2020-04-22),分享一句谚语 “积累知识、胜过..._wechaty使用教程

JQ基础知识大全-程序员宅基地

文章浏览阅读1w次,点赞32次,收藏231次。jQuery通俗来说,就是一个JS的库,里面封装了很多的JS方法,可以使前端人员去调用,大大减少了开发的时间,增加了开发的效率。JQ的优点轻量级,核心文件才几十KB,不会影响页面的加载速度。跨浏览器兼容。基本兼容了现在主流的浏览器。链式编程,隐式迭代。对事件,样式,动画支持,大大简化了DOM操作。支持插件扩展开发,有着丰富的第三方的插件,例如:轮播图,树形菜单,日期控件等免费,开源。JQ的顶级对象‘’$‘’是JQ的别称,代码中科院用JQ代替它$是JQ的顶级对象,相当于JS中的wi_jq

python卸载后环境变量还在_Python随笔:彻底卸载Python和清除Python缓存数据-程序员宅基地

文章浏览阅读4.4k次。1.前言当初安装Python时真是年少无知啊!把他安装在C盘了。现在C盘容量告急了,在卸载和迁移软件时发现不知不觉中Python已经快 1 GB 了。于是就打算卸载这个 Python ,在 D盘 再安装一个其他版本的。2.彻底卸载Python2.1首先在命令行窗口查一下Python的版本如上图,输入python回车,我的是3.7.3版本2.2然后就在这个镜像网站下载对应的安装包http://..._python卸载后环境变量还在

内网渗透之命令辅助_内网渗透命令-程序员宅基地

文章浏览阅读2.9k次。文章前言本篇文章主要介绍一些内网渗透中常用的命令~常用命令项目地址:GitHub - Al1ex/Pentest-Command: Pentest-Command相关扩展Wiindows渗透命令:WADComsLinux 命令:GTFOBins_内网渗透命令

Parted 分区_mkpart xfs-程序员宅基地

文章浏览阅读725次。1、查看硬盘信息[root@5gxx-2-32 ~]# fdisk -l #可以看到/dev/vdb 4TDisk /dev/vda: 42.9 GB, 42949672960 bytes, 83886080 sectorsUnits = sectors of 1 * 512 = 512 bytesSector size (logical/physical): 512 bytes / 512 bytesI/O size (minimum/optimal): 512 bytes / 512_mkpart xfs

随便推点

SpringBoot常见的经典面试题_springboot面试题-程序员宅基地

文章浏览阅读6k次,点赞6次,收藏53次。SpringBoot常见的经典面试题最近很多人面试时,简历上都说自己熟悉 Spring Boot, 或者说正在学习Spring Boot,一被面试官问道,都只停留在简单的使用阶段,很多东西都不清楚,下面我整理了一些springboot比较常见的面试题。1、什么是 Spring Boot?Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重xml的配置,提供了各种启动器,在运行过程中自定配置, _springboot面试题

PRODUCT_USER_PROFILE Table-程序员宅基地

文章浏览阅读134次。SQL*Plus uses the PRODUCT_USER_PROFILE (PUP) table, a table in the SYSTEM account, to provide product-level securi..._"sp2-0544: command \"select\" disabled in product user profile"

C++中CString,int,string,char*之间的转换_c++ cstring int-程序员宅基地

文章浏览阅读745次。《C++标准函数库》中说的 有三个函数可以将字符串的内容转换为字符数组和C—string 1.data(),返回没有”/0“的字符串数组 2,c_str(),返回有”/0“的字符串数组 3,copy() .................................................................int 转 CString:CString.Format("%d"_c++ cstring int

使用svn时出现Can't switch /XXX/XXX because it is not the repository yet_svn is not yet-程序员宅基地

文章浏览阅读1.7k次。问题描述出现的问题如题目所示。 翻译一下:不能选择这个目录,原因是这个目录还不是svn仓库。解决办法这个问题出现的原因是你将项目中的.svn文件夹删除了,一般情况你是可以通过撤销将文件恢复的。如果实在是没有办法恢复了,那么你只能重新checkout了。 如果你并没有将文件删除,那么就是因为你的项目还不是一个svn项目,要么就是你没有将项目导入到svn仓库中,要么就是你并不是使用..._svn is not yet

virsh链接虚拟机_virsh创建虚拟机-程序员宅基地

文章浏览阅读379次。1. 查看cpu是否支持虚拟化和 Xen 不同,KVM 需要有 CPU 的支持(Intel VT 或 AMD SVM),在安装 KVM 之前检查一下 CPU 是否提供了虚拟技术的支持2. 安装工具包3. 查看kvm是否安装成功4. 关闭防火墙5. 修改 qemu.conf 配置,把下面几个地方的注释去掉,然后把 dynamic_ownership 的值改成0,禁止 libvirtd 动态修改文件的..._virsh 连接虚拟机

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".-程序员宅基地

文章浏览阅读57次。SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".Spring Cloud 启动提示:SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: Defaulting to no-operation (NOP) logger imp..._"springcloud feign failed to load class \"org.slf4j.impl.staticloggerbinder\"."

推荐文章

热门文章

相关标签