求求你不要在使用BeanUtils进行拷贝了
  QHt7QJyNQUoA 2023年11月02日 56 0

哈喽,大家好,我是指北君

最近接手一个项目发现有些接口只是做了一些简单的单表查询业务,但是却耗时八百多毫秒,明显不太正常, 经排查发现时间都消耗在Apache的BeanUtils中对属性的拷贝上,整个流程使用了四次拷贝方法, 使得整个方法耗时急剧增加。指北君在这里求求大家不要再使用BeanUtils进行拷贝了,那使用什么呢?当然是今天的主角MapStruct。

如何引入

MapStruct引入相当简单。

如果你的项目使用Maven进行管理则添加以下配置:

<properties>
    <org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
<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>

如果你的项目使用Gradle进行管理则添加以下配置:

plugins {
    id 'net.ltgt.apt' version '0.20'
}
// 根据你的编译器选择
apply plugin: 'net.ltgt.apt-idea'
// or
apply plugin: 'net.ltgt.apt-eclipse'

dependencies {
    implementation "org.mapstruct:mapstruct:${mapstructVersion}"
    annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"

    // If you are using mapstruct in test code
    testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
}

这样就引入完成了。这里有两种使用方式,可以结合Spring进行使用,也可以不依赖Spring。如下示例:

如何使用

不依赖Spring方式

@Mapper
public interface CarMapper {
    //通过这个静态属性就能调用相应的方法
    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

    CarDto carToCarDto(Car car);
}

与Spring结合使用

@Mapper(componentModel = "spring")
public interface CarMapper {

    CarDto carToCarDto(Car car);
}

通过componentModel参数指定使用Spring的依赖注入方式使用。

当然了如果结合Spring使用也可以统一配置

如何转换

接下来我们进行简单的使用。相同字段自动映射,如果两个实体字段名相同,MapStruct就会自动通过相应的Getter/Setter生成属性拷贝方法, 并且自动忽略不相同的字段。使用方式和BeanUtils相似。

@Mapper
public interface CarMapper {
    CarDto carToCarDto(Car car);
}

MapStruct是在编译期对添加了@Mapper的接口进行生成的实现类,所以上面的代码会生成一个下面的实现类。是不是很简单呢,这样即保证了拷贝的性能,同时我们不需要在业务代码中写对应的拷贝方法,岂不美哉。

当然了,这时候你可能会想,那如果我两个实体映射时有些字段名不一致怎么办呢?

@Mapper
public interface CarMapper {
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
}

我们可以通过在方法上添加@Mapping注解指定以下映射关系就可以了,来我们看下效果吧。可以看到我们指定的把numberOfSeats映射成seatCount也很简单的完成了。

MapStruct还有一个更加强大的功能,就是如果映射的类型不一样可以自动转换,常见的数据类型几乎都能支持。我们也一起来看一下吧。

从图中我们可以看到其实并不是很智能,而且转换可能并不是我们想要的,比如说我们要把一个 String类型的时间戳转换为Date类型,并且可能存在BUG,比如说我们想要把String类型的价格转换为BigDecimal类型, 如果这个Price是一个空字符串,那么这里就会报错。

那这怎么办呢?指北君在最后给大家准备了我演示的完整源码和教程。像集合的映射,实体反映射,自定义映射规则,填充默认值等等,教程都有写。

我们已经了解了如何使用,那么真正的效果如何呢?

效果如何

指北君在这里也给大家精心准备了各个拷贝方式的直观对比图。

编辑搜图

从图中可以看到,使用MapStruct提升速度的效果还是蛮显著的。

指北君有话说

当然了MapStruct的骚操作还是挺多的,几乎能满足90%的需要。剩下的10%很大可能是你设计有问题。但由于内容太多,全部展示出来有点啰嗦。如果你感兴趣可以按照下面提供的方式获取相关资源。如果有什么不明白的地方也可以在群里直接咨询我们。指北君也是知无不言言无不尽。

关注公众号 [程序员了不起]回复[eee113] 即可获取navicat绿版。


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  LxKByvFwtHdi   2023年11月02日   62   0   0 xml生命周期maven
  LxKByvFwtHdi   2023年11月02日   41   0   0 主键字段spring
  lh6O4DgR0ZQ8   2023年11月22日   26   0   0 Memory字段sed
  ILwIY8Berufg   2023年11月02日   52   0   0 idesedmaven
  cnCTZTo8tgOC   2023年11月02日   34   0   0 父类字段子类
  nFFsDmyUFj1S   2023年11月05日   53   0   0 ide安装配置maven
  LxKByvFwtHdi   2023年11月02日   42   0   0 主键字段外键
QHt7QJyNQUoA