跳转至

阿里为什么不推荐 Apache BeanUtils 做属性复制?

结论

Apache Commons BeanUtils 慢、不安全、抛受检异常,阿里规范明确禁止。推荐使用 Spring BeanUtils、MapStruct、Orika、Cglib BeanCopier

一、Apache BeanUtils 为什么慢

1. 每次都做反射 + 类型转换

BeanUtils.copyProperties(dest, orig) 内部:

  • 用反射读 getter/setter。
  • 每次都 new ConvertUtilsConverter
  • 做字符串类型转换(如 String → Integer)。
  • 大量对象创建。

实测:拷贝 1 万次简单对象,Apache BeanUtils 比 Spring BeanUtils 慢 几十倍

2. 大量异常处理

内部用异常控制流程,热路径上频繁抛/捕获异常。

二、常见坑

1. 类型不匹配会抛受检异常

try {
    BeanUtils.copyProperties(userDTO, userDO);
} catch (IllegalAccessException | InvocationTargetException e) {
    throw new RuntimeException(e);
}

必须 try-catch,否则编译不过。

2. 浅拷贝问题

引用类型字段(List、Map)只复制引用,改一边另一边也变。

3. 包装类与基本类型不一致

IntegerintLonglong 可能转换失败或丢精度。

4. 性能随字段数增加急剧下降

三、替代方案对比

工具 原理 性能 灵活性
Apache BeanUtils 反射 + 转换 极慢 一般
Spring BeanUtils 反射 快(比 Apache 快很多) 一般
Cglib BeanCopier 动态生成字节码 很快 简单映射
MapStruct 编译期生成代码 最快(无反射) 灵活,支持自定义映射
Orika 字节码生成

四、推荐写法

Spring BeanUtils(最常用)

import org.springframework.beans.BeanUtils;

BeanUtils.copyProperties(source, target);
// 注意:Spring 是 copyProperties(source, target),Apache 是 (target, source),顺序相反!

MapStruct(编译期生成,推荐)

@Mapper
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);

    @Mapping(source = "userName", target = "name")
    UserDTO toDTO(UserDO user);
}

编译期生成实现类,无反射,最快。

Spring vs Apache 顺序坑

  • Spring:BeanUtils.copyProperties(source, target)
  • Apache:BeanUtils.copyProperties(target, source) 两者参数顺序相反,引错包就是灾难。

阿里规范原文

为了避免版本和性能问题,避免使用 Apache BeanUtils。推荐使用 Spring 的 BeanUtils,或者直接使用 getter/setter。