阿里为什么不推荐 Apache BeanUtils 做属性复制?¶
结论¶
Apache Commons BeanUtils 慢、不安全、抛受检异常,阿里规范明确禁止。推荐使用 Spring BeanUtils、MapStruct、Orika、Cglib BeanCopier。
一、Apache BeanUtils 为什么慢¶
1. 每次都做反射 + 类型转换¶
BeanUtils.copyProperties(dest, orig) 内部:
- 用反射读 getter/setter。
- 每次都 new
ConvertUtils、Converter。 - 做字符串类型转换(如 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. 包装类与基本类型不一致¶
Integer 与 int、Long 与 long 可能转换失败或丢精度。
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。