背景

众所周知,Spring框架提供了著名的类拷贝工具BeanUtils,其使用简单、方便。

但是其内部实现基于运行时反射进行同名属性拷贝,对于需要进行字段映射和高性能运行时场景都无法满足要求,因此,开源社区衍生出了以下的类拷贝框架

使用

目前第二代密码管理平台采用的是mica重写后的BeanUtil类拷贝方法,整体来说性能可以满足要求,但为了进一步提升查询时类转换性能,同时考虑到应用中本身就包含了swagger依赖,而swagger中本身就引入了mapstruct依赖包,故无需引入新依赖。

因此,最终考虑将业务场景下的类拷贝改成Mapstruct实现,由此开始mapstruct爬坑之路。。

软件包版本

SpringBoot:2.4.2

SpringCloud:2020.0.1

lombok: 1.18.16

mapstruct: 1.3.1.Final

新增依赖

由于需要由mapstruct根据定义的接口自动生成类转换实现,故需要引入mapstruct接口处理包,有两种方式,一种通过maven插件的形式,一种通过maven依赖的形式

maven插件的形式(官方推荐)

<build>
    <plugins>
        <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>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

maven依赖的形式

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${mapstruct.version}</version>
</dependency>

考虑到平台的maven模块数量较大,互相依赖关系比较复杂,遗漏插件依赖容易导致难以排查的bug,最终采用第二种方式,减少编码量。

新增映射类

/**
 * @author F嘉阳
 * @date 2021/2/18 12:06
 */
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface OssEntityMapper {

    OssVO ossToOssVO(Oss oss);

    OssDTO ossToOssDto(Oss oss);

    /**
     * bladeFile转Oss
     *
     * @param bladeFile
     * @return
     */
    @Mapping(target = "fileName", source = "name")
    Oss bladeFileToOss(BladeFile bladeFile);
}

新增单元测试

由于首次使用mapstruct,编写单元测试检测代码是否成功

/**
 * @author F嘉阳
 * @date 2021/2/18 14:23
 */
@Slf4j
@ExtendWith(BladeSpringExtension.class)
@SpringBootTest(classes = ResourceApplication.class)
@BladeBootTest(appName = AppConstant.APPLICATION_RESOURCE_NAME, profile = "test", enableLoader = true)
class OssEntityMapperTest {

    @Autowired
    private OssEntityMapper mapper;

    @Test
    void ossToOssVO() {
        Oss oss = new Oss();
        oss.setFileName("filename");
        oss.setLink("link");
        oss.setOriginalName("origin");
        oss.setOssType(OssType.QI_NIU_OSS);
        oss.setIsDeleted(BladeConstant.DB_NOT_DELETED);
        OssVO ossVO = mapper.ossToOssVO(oss);
        assertEquals("filename", ossVO.getFileName());
        assertEquals("link", ossVO.getLink());
        assertEquals("origin", ossVO.getOriginalName());
    }

    @Test
    void ossToOssDto() {
        Oss oss = new Oss();
        oss.setFileName("filename");
        oss.setLink("link");
        oss.setOriginalName("origin");
        oss.setOssType(OssType.QI_NIU_OSS);
        oss.setIsDeleted(BladeConstant.DB_NOT_DELETED);
        OssDTO ossDTO = mapper.ossToOssDto(oss);
        assertEquals("filename", ossDTO.getFileName());
        assertEquals("link", ossDTO.getLink());
        assertEquals("origin", ossDTO.getOriginalName());
    }

    @Test
    void bladeFileToOss() {
        BladeFile file = new BladeFile();
        file.setName("file");
        file.setLink("link");
        file.setOriginalName("origin");
        Oss oss = mapper.bladeFileToOss(file);
        assertEquals("file", oss.getFileName());
        assertEquals("link", oss.getLink());
        assertEquals("origin", oss.getOriginalName());
    }
}

编译结果

编译结果,报错

1613831019363

这就很奇怪了,这是按官网最简单的方式写的,而且IDEA也安装了mapstruct插件,也没报错,能正确识别属性,怎么编译就不通过了

查看生成的代码,确实没有属性映射

爬坑之路

由于编译报错,考虑以下方法解决

  1. 移除spring容器依赖,采用类引用的方式编写。——无效
  2. 移除属性映射,尝试编译,可以编译通过,但生成的代码没有同名属性映射,只会返回空值。——无效

    @Override
    public Oss bladeFileToOss(BladeFile bladeFile) {
        if ( bladeFile == null ) {
            return null;
        }
    
        Oss oss = new Oss();
    
        return oss;
    }
  3. 更换类名后测试通过,多次测试仍然有不通过的情况。——可能和编译环境有关?
  4. 相同代码放在单独的工程(非maven多模块)测试通过。——可能和maven多模块依赖有关?
  5. 根据网上检索的结果,普遍反映的lombok版本太低导致的,经过检查,当前的lombok版本应该是满足最低版本要求的。——无效
  6. 考虑是否和spring某些组件冲突,单独新开maven多模块工程测试,依然测试不通过。——果然和maven有关?

解决方法

最终通过给官方仓库提交issue咨询得到答案

1613831616294

原来是lombok版本太高导致的。。

最终降低lombok版本问题解决。

Last modification:February 20th, 2021 at 10:45 pm
如果觉得我的文章对你有用,请随意赞赏