麻了!不要再动不动就用BeanUtil.copyProperties了!

简介: 麻了!不要再动不动就用BeanUtil.copyProperties了!


前言

最近项目上要求升级一个工具包hutool的版本,以解决安全漏洞问题,这不升级还好,一升级反而捅出了更大的篓子,究竟是怎么回事呢?

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

事件回顾

我们项目原先使用的hutool版本是5.7.2,在代码中,我们的数据传输对象DTO和数据实体对象中大量使用了工具包中的BeanUtil.copyProperties(), 大体代码如下:

  1. 数据传输对象
@Data
@ToString
public class DiagramDTO {
    // 前端生产的字符串id
    private String id;
    private String code;
    private String name;
}
  1. 数据实体对象
@Data
@ToString
public class Diagram {
    private Integer id;
    private String code;
    private String name;
}
  1. 业务逻辑
public class BeanCopyTest {
    public static void main(String[] args) {
        // 前端传输的对象
        DiagramDTO diagramDTO = new DiagramDTO();
        // 如果前端传入的id事包含e的,升级后就会报错
        diagramDTO.setId("3em3dgqsgmn0");
        diagramDTO.setCode("d1");
        diagramDTO.setName("图表");
        Diagram diagram = new Diagram();
        // 关键点,数据拷贝
        BeanUtil.copyProperties(diagramDTO, diagram);
        System.out.println("数据实体对象:" + diagram);
        //设置id为空,自增
        diagram.setId(null);
        //保存到数据库中 TODO
        //diagramMapper.save(diagram);
    }
}

升级前,hutool是5.7.2版本下,执行结果如下图。

  • BeanUtil.copyProperties虽然字段类型不一样,但是做了兼容处理,所以业务没有影响业务逻辑。

升级后,hutool是5.8.8版本,执行结果如下图所示:

  • 执行报错,因为升级后的版本修改了实现,增加了下面的逻辑,如果包含E, 就会抛错,从而影响了业务逻辑,同时这个id是否包含e又是随机因素,到了生产才发现,就悲剧了。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

分析探讨

我发现大部分人写代码都喜欢偷懒,在上面的场景中,虽然BeanUtil.copyProperties用的一时爽,但有时候带来的后果是很严重的,所以很不推荐这种方式。为什么这么说呢?

比如团队中的某些人偷偷改了数据传输对象DTO,比如修改了类型、删去了某个字段。用BeanUtil.copyProperties的方式压根无法在编译阶段发现,更别提修改的影响范围了,这就只能把风险暴露到生产上去了。那有什么更好的方法呢?

推荐方案

  1. 原始的getset方式

我是比较推崇这种做法的,比如现在DiagramDTO删去某个字段,编译器就会报错,就会引起你的注意了,让问题提前暴露,无处遁形。

你可能觉得站着说话不腰疼,字段少好,如果字段很多还不得写死啊,我这里推荐一个IDEA的插件,可以帮你智能生成这样的代码。

话不多说,自己玩儿去~~

  1. 使用开源库ModelMapper

ModelMapper是一个开源库,可以很方便、简单地将对象从一种类型映射到另一种类型,底层是通过反射来自动确定对象之间的映射,还可以自定义映射规则。

private static void testModelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        DiagramDTO diagramDTO = new DiagramDTO();
        diagramDTO.setId("3em3dgqsgmn0");
        diagramDTO.setCode("d1");
        diagramDTO.setName("图表");
        Diagram diagram = modelMapper.map(diagramDTO, Diagram.class);
    }
  1. 使用开源库MapStruct

MapStruct也是Java中另外一个用于映射对象很流行的开源工具。它是在编译阶段生成对应的映射代码,相对于ModelMapper底层放射的方案,性能更好。

@Mapper
public interface DiagramMapper {
    DiagramMapper INSTANCE = Mappers.getMapper(DiagramMapper.class);
    DiagramDTO toDTO(Diagram diagram);
    Diagram toEntity(DiagramDTO diagram);
}
private static void testMapStruct() {
    DiagramDTO diagramDTO = new DiagramDTO();
    diagramDTO.setId("3em3dgqsgmn0");
    diagramDTO.setCode("d1");
    diagramDTO.setName("图表");
    Diagram diagram = DiagramMapper.INSTANCE.toEntity(diagramDTO);
}
  • DiagramMapper接口使用了@Mapper注解,用来表明使用MapStruct处理
  • MapStruct中更多高级特性大家自己探索一下。

总结

小结一下,对象在不同层之间进行转换映射,很不建议使用BeanUtil.copyProperties这种方式,更加推荐使用原生的set, get方式,不容易出错。当然这不是将BeanUtil.copyProperties一棒子打死,毫无用武之地,在特定场景,比如方法内部对象的转换等影响小的范围还是很方便的。

相关文章
|
Java 数据库连接 数据库
探究Java中的MyBatis Plus注解 @TableField:灵活处理字段映射与自动填充
在数据库操作中,字段映射和字段填充是开发者经常需要处理的问题。MyBatis Plus作为一款优秀的ORM框架,提供了注解 `@TableField`,能够帮助开发者更加灵活地处理字段映射,以及在特定场景下实现自动填充功能。本文将深入探讨 `@TableField` 注解的用法及其在持久层开发中的应用。
5893 1
|
Java Maven
Maven 在 pom.xml 文件中配置 repositories 仓库
如果你希望在你的项目中使用独立的 repositories 。 例如,你希望配置使用自己的 https://maven.ossez.com/repository/internal 作为仓库。 例如,修改后的 pom.
8437 0
|
安全 Java 数据库
SpringSecurity实现多种登录方式,如邮件验证码、电话号码登录
SpringSecurity实现多种登录方式,如邮件验证码、电话号码登录
3029 2
|
前端开发 安全 Java
Spring Boot项目中VO层设计:选择继承或组合的灵活实践
Spring Boot项目中VO层设计:选择继承或组合的灵活实践
942 0
|
Docker 容器
docker compose logs 常用指令
`docker compose logs`命令用于查看容器的输出日志。该命令提供多种选项,如以纯文本形式输出(`--no-color`)、隐藏日志前缀(`--no-log-prefix`)、显示时间戳(`-t, --timestamps`)等。用户可以指定时间范围(如`--since "2024-08-06T13:11:10"`和`--until "2024-08-06T13:11:15"`)或最近几分钟内的日志(如`--since 5m`)。使用`-f, --follow`可实时跟踪日志输出。注意:参数-h已废弃,应使用--help。
1028 57
|
存储 SQL 关系型数据库
MySQL 给查询结果增列并自定义列数据
MySQL 给查询结果增列并自定义列数据
1773 2
|
数据采集 机器学习/深度学习 自然语言处理
ModelScope模型库体验之中文StructBERT系列预训练语言模型
StructBERT在BERT的基础上提出改进优化,通过在句子级别和词级别引入两个新的目标函数,打乱句子/词的顺序并使模型对其进行还原的方式,能让机器更好地掌握人类语法,加深对自然语言的理解,使得模型学习到更强的语言结构信息。
47990 0
ModelScope模型库体验之中文StructBERT系列预训练语言模型
|
消息中间件 负载均衡 Apache
【RocketMQ系列七】消费者和生产者的实现细节
【RocketMQ系列七】消费者和生产者的实现细节
516 1
java.sql.SQLException: sql injection violation
本文目录 1. 报错信息 2. 问题分析 3. 排除法 4. 解决方案
4143 0
|
存储 IDE C#
C#入门:在JetBrains Rider中创建.Net Framework控制台应用程序,输出“Hello, World!”
C#入门:在JetBrains Rider中创建.Net Framework控制台应用程序,输出“Hello, World!”
1730 0

热门文章

最新文章