Java Bean所有拷贝方式使用方法及性能比较详解
目录
- 一、总体概述及比较
- 1. 手动拷贝
- 2. Java BeanUtils 工具类
- 2.1 Apache Commons BeanUtils
- 2.2 Spring BeanUtils
- 3. CGLIB BeanCopier
- 4. MapStruct
- 5. Dozer
- 6. ModelMapper
- 7. Lombok @Builder/@Data + 构造方法
- 8. jsON序列化反序列化
- 9. 总结对比表
- 10. 选择建议
- 二、使用详解
- 1、Spring BeanUtils 详细用法
- 1.1. 基本用法
- 1.2. 拷贝部分属性
- 1.3. 常见问题
- 2、Apache Commons BeanUtils 详细用法
- 2.1. 基本用法
- 2.2. 类型转换
- 2.3. 性能问题
- 3、CGLIB BeanCopier 进阶用法
- 3.1. 基本用法
- 3.2. 支持自定义转换
- 3.3. 性能优势
- 4、MapStruct 进阶用法
- 4.1. 基本映射
- 4.2. 字段名不一致映射
- 4.3. 自定义类型转换
- 4.4. 集合转换
- 4.5. 注意事项
- 5、Dozer/ModelMapper 进阶用法
- 5.1. Dozer XML 配置
- 5.2. ModelMapper 映射规则
- 5.3. 支持复杂对象和嵌套属性
- 6、JSON序列化方式进阶
- 6.1. Jackson
- 6.2. Gson
- 6.3. 注意事项
- 7、Lombok Builder/构造方法进阶
- 7.1. Lombok简化代码
- 7.2. 适合场景
- 8、实战建议
- 9、注意事项与最佳实践
- 三、常见问题与解决办法
- 1. 字段名不一致
- 2. 类型不一致
- 3. 嵌套对象或集合
- 4. 性能瓶颈
- 5. Null值处理
- 四、性能对比与实测
- 五、实际项目中的选择建议
- 六、自定义拷贝场景举例
- 1. 只拷贝非null字段
- 2. 字段名映射(MapStruct示例)
- 3. 嵌套对象拷贝(MapStruct示例)
- 七、最佳实践
- 八、参考工具封装(示例)
一、总体概述及比较
1. 手动拷贝
方式说明最原始也最灵活的方法,直接通过setter和getter手动赋值。
示例代码
TargetBean target = new TargetBean(); target.setName(source.getName()); target.setAge(source.getAge());
优点
- 灵活性高,可以自定义转换逻辑
- 不依赖第三方库
缺点
- 代码冗余,维护成本高
- 属性多时工作量大,易出错
适用场景
- 属性复杂、转换逻辑多样时
- 对性能和依赖有严格要求时
2. Java BeanUtils 工具类
2.1 Apache Commons BeanUtils
方式说明使用 org.apache.commons.beanutils.BeanUtils 进行属性拷贝。
示例代码
import org.apache.commons.beanutils.BeanUtils; BeanUtils.copyProperties(target, source);
优点
- 简单易用
- 支持不同类型的对象拷贝
缺点
- 依赖反射,性能较低
- 不支持基本类型,全部按字符串处理,类型转换有限
- 不支持嵌套对象深拷贝
适用场景
- 属性简单、性能要求不高的场合
2.2 Spring BeanUtils
方式说明使用 org.springframework.beans.BeanUtils 进行属性拷贝。
示例代码
import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(source, target);
优点
- 性能比 Apache BeanUtils 略高
- 支持基本类型
- Spring 项目中开箱即用
缺点
- 依然是浅拷贝,不支持嵌套对象
- 对属性名和类型要求严格
适用场景
- Spring 项目中,简单Bean转换
3. CGLIB BeanCopier
方式说明使用 CGLIB 的 BeanCopier,通过动态生成字节码实现高性能拷贝。
示例代码
import net.sf.cglib.beans.BeanCopier; BeanCopier copier = BeanCopier.create(Source.class, Target.class, false); copier.copy(source, target, null);
优点
- 性能高于反射方式
- 支持同名同类型属性的快速拷贝
缺点
- 依赖CGLIB库
- 不支持类型转换和嵌套对象
- 代码略繁琐
适用场景
- 需要批量高性能拷贝的场景
4. MapStruct
方式说明MapStruct 是编译期代码生成的Bean拷贝工具,支持复杂映射和自定义转换。
示例代码
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
TargetBean toTargetBean(SourceBean source);
}
使用:
TargetBean target = UserMapper.INSTANCE.toTargetBean(source);
优点
- 编译期生成代码,性能极高
- 支持复杂属性映射、类型转换
- 可自定义字段映射规则
缺点
- 需要编写接口和注解,学习成本略高
- 需要编译期插件支持
适用场景
- 复杂对象转换、大型项目、性能敏感场合
5. Dozer
方式说明Dozer 是一个支持深度拷贝的 Java Bean 到 Java Bean 的映射工具。
示例代码
Mapper mapper = new DozerBeanMapper(); TargetBean target = mapper.map(source, TargetBean.class);
优点
- 支持深度拷贝
- 支持复杂类型映射
- 支持XML配置映射规则
缺点
- 性能一般,反射实现
- 配置和调试较复杂
适用场景
- 需要深度拷贝和复杂映射的场景
6. ModelMapper
方式说明ModelMapper 也是一个Java Bean映射工具,支持复杂映射和类型转换。
示例代码
ModelMapper modelMapper = new ModelMapper(); TargetBean target = modelMapper.map(source, TargetBean.class);
优点
- 支持复杂映射和深拷贝
- 支持自定义映射规则
缺点
- 性能一般,反射实现
- 配置略复杂
适用场景
- 对转换灵活性要求较高的场景
7. Lombok @Builder/@Data + 构造方法
方式说明利用 Lombok 注解简化代码,通过构造方法或Builder模式实现对象转换。
示例代码
TargetBean target = TargetBean.builder()
.name(source.getName())
.age(source.getAge())
.build();
优点
- 代码简洁
- 可自定义转换逻辑
缺点
- 需手动指定字段
- 适合属性较少、转换逻辑简单时
8. JSON序列化反序列化
方式说明通过Jackson、Gson等将对象转为JSON再反序列化为目标类型。
示例代码
ObjectMapper mapper = new ObjectMapper(); TargetBean target = mapper.readValue(mapper.writeValueAsString(source), TargetBean.class);
优点
- 支持深度拷贝
- 属性名不一致时可通过注解映射
缺点
- 性能较低
- 依赖第三方库
- 不适合大批量高频率场景
9. 总结对比表
| 方式 | 性能 | 深拷贝 | 复杂映射 | 依赖库 | 适用场景 |
|---|---|---|---|---|---|
| 手动拷贝 | 高 | 支持 | 支持 | 无 | 灵活性强、属性少 |
| BeanUtils | 低 | 否 | 否 | commons/spring | 简单Bean拷贝 |
| CGLIB BeanCopier | 高 | 否 | 否 | cglib | 大批量拷贝 |
| MapStruct | 极高 | 支持 | 支持 | mapstruct | 大型项目、复杂转换 |
| Dozer | 一般 | 支持 | 支持 | dozer | 深拷贝、复杂映射 |
| ModelMapper | 一般 | 支持 | 支持 | modelmapper | 灵活映射 |
| Lombok Builder | 高 | 否 | 支持 | lombok | 属性少、代码简洁 |
| JSON序列化 | 低 | 支持 | 支持 | jackson/gson | 临时、简单深拷贝 |
10. 选择建议
- 简单场景:Spring BeanUtils、CGLIB BeanCopier
- 复杂映射:MapStruct、Dozer、ModelMapper
- 深拷贝:Dozer、ModelMapper、JSON序列化
- 性能敏感:MapStruct、CGLIB BeanCopier、手动拷贝
- 灵活性高:手动拷贝、Lombok Builder
二、使用详解
1、Spring BeanUtils 详细用法
1.1. 基本用法
import org.springframework.beans.BeanUtils; SourceBean source = new SourceBean(); TargetBean target = new TargetBean(); BeanUtils.copyProperties(source, target);
注意:属性名和类型需完全一致,且只做浅拷贝(即对象属性只拷贝引用)。
1.2. 拷贝部分属性
可以指定忽略某些属性:
BeanUtils.copyProperties(source, target, "password", "id");
1.3. 常见问题
- 不支持深拷贝:嵌套对象只拷贝引用。
- 类型不兼容会报错:如
int和String不能自动转换。
2、Apache Commons BeanUtils 详细用法
2.1. 基本用法
import org.apache.commons.beanutils.BeanUtils; BeanUtils.copyProperties(target, source);
2.2. 类型转换
pythonBeanUtils会自动做一些类型转换(如 String 转 int),但不支持复杂类型。
2.3. 性能问题
BeanUtils 采用反射,性能较低,不推荐在高频场景下使用。
3、CGLIB BeanCopier 进阶用法
3.1. 基本用法
BeanCopier copier = BeanCopier.create(SourceBean.class, Targhttp://www.devze.cometBean.class, false); copier.copy(source, target, null);
3.2. 支持自定义转换
BeanCopier copier = BeanCopier.create(SourceBean.class, TargetBean.class, true);
copier.copy(source, target, (value, targetType, context) -> {
// 自定义转换逻辑
if (value instanceof String && targetType == Integer.class) {
return Integer.valueOf((String)value);
}
return value;
});
3.3. 性能优势
BeanCopier会动态生成字节码,性能极高,适合大批量拷贝。
4、MapStruct 进阶用法
4.1. 基本映射
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
TargetBean toTargetBean(SourceBean source);
}
4.2. 字段名不一致映射
@Mapper
public interface UserMapper {
@Mapping(source = "username", target = "name")
TargetBean toTargetBean(SourceBean source);
}
4.3. 自定义类型转换
@Mapper
public interface UserMapper {
@Mapping(source = "age", target = "ageStr")
TargetBean toTargetBean(SourceBean source);
default String intToString(int age) {
return String.valueOf(age);
}
}
4.4. 集合转换
List<TargetBean> toTargetBeanList(List<SourceBean> sourceList);
4.5. 注意事项
- 需要在IDE或maven插件中开启注解处理器。
- 生成代码后可直接调用,性能极高。
5、Dozer/ModelMapper 进阶用法
5.1. Dozer XML 配置
<mapping>
<class-a>com.example.SourceBean</class-a>
<class-b>com.example.TargetBean</class-b>
<field>
<a>username</a>
name
</field>
</mapping>
5.2. ModelMapper 映射规则
modelMapper.typeMap(SourceBean.class, TargetBean.class)
.addMappings(mapper -> mapper.map(SourceBean::getUsername, TargetBean::setName));
5.3. 支持复杂对象和嵌套属性
适合复杂领域模型转换,但性能一般。
6、JSON序列化方式进阶
6.1. Jackson
ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(source); TargetBean target = mapper.readValue(json, TargetBean.class);
6.2. Gson
Gson gson = new Gson(); String json = gson.toJson(source); TargetBean target = gson.fromJson(json, TargetBean.class);
6.3. 注意事项
- 适合临时转换或深拷贝,但性能较低。
- 可通过注解如
@JsonProperty实现字段名映射。
7、Lombok Builder/构造方法进阶
7.1. Lombok简化代码
@Getter
@Setter
@Builder
public class TargetBean {
private String name;
private int age;
}
拷贝:
TargetBean target = TargetBean.builder()
.name(source.getName())
.age(source.getAge())
.build();
7.2. 适合场景
- 属性较少、转换逻辑简单时
- 代码可读性高,维护方便
8、实战建议
- 简单DTO/VO转换:优先用Spring BeanUtils或CGLIB BeanCopier。
- 复杂/多字段/类型转换:优先MapStruct。
- 深拷贝或嵌套对象:Dozer、ModelMapper或JSON序列化。
- 性能敏感批量转换:CGLIB BeanCopier、MapStruct。
- 自定义转换逻辑多:手动拷贝或MapStruct自定义方法。
9、注意事项与最佳实践
- 避免在高并发场景用反射类工具(BeanUtils、Dozer等)。
- 字段名/类型不一致要选支持自定义映射的工具(MapStruct、Dozer、ModelMapper)。
- 拷贝前后对象不可互相影响(深拷贝需求)时要选支持深拷贝的工具。
- 转换逻辑复杂时建议写单元测试,避免数据丢失或错误。
三、常见问题与解决办法
1. 字段名不一致
问题:源对象和目标对象字段名不同,常规工具(如Spring BeanUtils)无法自动拷贝。
解决方案:
- MapStruct支持@Mapping注解自定义字段映射。
- Dozer和ModelMapper可通过配置或API自定义映射。
- 手动拷贝最灵活,直接赋值。
2. 类型不一致
问题:如源对象字段为String,目标对象为Integer。
解决方案:
- MapStruct可自定义转换方法。
- CGLIB BeanCopier可通过转换器接口自定义类型转换。
- ModelMapper支持自定义转换规则。
3. 嵌套对象或集合
问题:如源对象有嵌套对象或集合,浅拷贝只拷贝引用。
解决方案:
- Dozer、ModelMapper、JSON序列化支持深拷贝。
- MapStruct支持嵌套对象和集合的转换(可递归映射)。
4. 性能瓶颈
问题:反射类工具在大数据量场景下性能不足。
解决方案:
- MapStruct、CGLIB BeanCopier编译期或字节码级别实现,性能极高。
- 手动拷贝性能最佳,但开发成本高。
5. Null值处理
问题:拷贝时源对象字段为null,目标对象是否需要覆盖?
解决方案:
- BeanUtils默认会拷贝null,可以通过扩展或自定义工具类实现跳过null。
- MapStruct支持@Mapping(target = “xxx”, ignore = true)跳过指定字段。
四、性能对比与实测
| 工具 | 性能(高/中/低) | 备注 |
|---|---|---|
| 手动拷贝 | 极高 | 无反射,代码量大 |
| CGLIB BeanCopier | 极高 | 字节码生成,适合高并发 |
| MapStruct | 极高 | 编译期生成,适合复杂场景 |
| Spring BeanUtils | 低~中 | 反射实现,适合简单场景 |
| Apache BeanUtils | 低 | 反射+类型转换,性能最差 |
| Dozer/ModelMapper | 中 | 反射+复杂映射,灵活性高 |
| JSON序列化 | 低 | 适合临时深拷贝,性能低 |
实测建议:
- 单次拷贝或属性少时性能差异不明显,批量场景下优先选MapStruct或CGLIB BeanCopier。
- MapStruct适合字段复杂、类型多样、性能敏感场景。
- BeanUtils适合简单、快速开发和原型验证。
五、实际项目中的选择建议
- 微服务、领域模型转换:推荐MapStruct(如DTO→Entity、VO→DTO)。
- Spring项目简单Bean拷贝:用Spring BeanUtils。
- 需要深拷贝/嵌套对象:Dozer、ModelMapper或JSON序列化。
- 批量高性能场景:CGLIB BeanCopier或MapStruct。
- 自定义字段、类型转换复杂:MapStruct或手动拷贝。
六、自定义拷贝场景举例
1. 只拷贝非null字段
public static void copyNonNullProperties(Object src, Object target) {
BeanWrapper srcWrap = new BeanWrapperImpl(src);
BeanWrapper trgWrap = new BeanWrapperImpl(target);
for (PropertyDescriptor pd : srcWrap.getPropertyDescriptors()) {
Object value = srcWrap.getPropertyValue(pd.getName());
if (value != null) {
trgWrap.setPropertyValue(pd.getName(), value);
}
}
}
2. 字段名映射(MapStruct示例)
@Mapper
public interface MyMapper {
@Mappings({
@Mapping(source = "userName", target = "name"),
@Mapping(source = "userAge", target = "age")
})
TargetBean toTarget(SourceBean source);
}
3. 嵌套对象拷贝(MapStruct示例)
@Mapper
public interface OrderMapper {
OrderDTO toDTO(Order order);
AddressDTO addressToDTO(Address address);
}
MapStruct会自动递归调用addressToDTO。
七、最佳实践
- 统一转换入口:在项目中建议统一封装sdCGKVfBean拷贝工具类,便于维护和扩展。
- 单元测试覆盖:复杂转换逻辑务必写单元测试,防止属性丢失或错误。
- 性能评估:批量转换场景建议实际测评不同工具性能。
- 异常处理:注意捕获和处理转换异常,避免因拷贝失败影响业务流程。
八、参考工具封装(示例)
public class BeanCopyUtil {
public static <T> T copy(Object source, Class<T>sdCGKVf; clazz) {
try {
T target = clazz.newInstance();
BeanUtils.copyProperties(source, target);
return target;
} catch (Exception e) {
throw new RuntimeException("Bean copy failed", e);
}
}
}
可根据实际需要扩展支持MapStruct、CGLIB等。
到此这篇关于Java Bean所有拷贝方式使用方法及性能比较详解的文章就介绍到这了,更多相关Java Bean所有拷贝方式使用内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.jscppcns.com)!
加载中,请稍侯......
精彩评论