Java Bean所有拷贝方式使用方法及性能比较详解

 更新时间:2025年10月22日 09:52:35   作者:猩火燎猿  
在Java开发中,常常需要将一个对象的属性值拷贝到另一个对象中,下面这篇文章主要介绍了Java Bean所有拷贝方式使用方法及性能比较的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、总体概述及比较

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. 总结对比表

方式性能深拷贝复杂映射依赖库适用场景
手动拷贝支持支持灵活性强、属性少
BeanUtilscommons/spring简单Bean拷贝
CGLIB BeanCopiercglib大批量拷贝
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. 类型转换

BeanUtils会自动做一些类型转换(如 String 转 int),但不支持复杂类型。

2.3. 性能问题

BeanUtils 采用反射,性能较低,不推荐在高频场景下使用

3、CGLIB BeanCopier 进阶用法

3.1. 基本用法

BeanCopier copier = BeanCopier.create(SourceBean.class, TargetBean.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>
        <b>name</b>
    </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注解自定义字段映射。
  • DozerModelMapper可通过配置或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。

七、最佳实践

  • 统一转换入口:在项目中建议统一封装Bean拷贝工具类,便于维护和扩展。
  • 单元测试覆盖:复杂转换逻辑务必写单元测试,防止属性丢失或错误。
  • 性能评估:批量转换场景建议实际测评不同工具性能。
  • 异常处理:注意捕获和处理转换异常,避免因拷贝失败影响业务流程。

八、参考工具封装(示例)

public class BeanCopyUtil {
    public static <T> T copy(Object source, Class<T> 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所有拷贝方式使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java项目中classpath类路径是什么

    Java项目中classpath类路径是什么

    classpath指的是类路径,也就是编译之后的target文件夹下的WEB-INF/class文件夹,下面这篇文章主要给大家介绍了关于Java项目中classpath类路径是什么的相关资料,需要的朋友可以参考下
    2023-02-02
  • idea使用mybatis插件mapper中的方法爆红的解决方案

    idea使用mybatis插件mapper中的方法爆红的解决方案

    这篇文章主要介绍了idea使用mybatis插件mapper中的方法爆红的解决方案,文中给出了详细的原因分析和解决方案,对大家解决问题有一定的帮助,需要的朋友可以参考下
    2024-07-07
  • 基于spring+hibernate+JQuery开发之电子相册(附源码下载)

    基于spring+hibernate+JQuery开发之电子相册(附源码下载)

    本篇文章介绍了,基于spring+hibernate+JQuery开发之电子相册(附源码下载)。需要的朋友参考下
    2013-05-05
  • elasticsearch索引index数据功能源码示例

    elasticsearch索引index数据功能源码示例

    这篇文章主要为大家介绍了elasticsearch索引index功能源码示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • Spring MVC和springboot静态资源处理问题

    Spring MVC和springboot静态资源处理问题

    这篇文章主要介绍了Spring MVC和springboot静态资源处理问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • Java 阻塞队列和线程池原理分析

    Java 阻塞队列和线程池原理分析

    这篇文章主要介绍了Java 阻塞队列和线程池原理分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java使用itext5实现生成多个PDF并合并

    Java使用itext5实现生成多个PDF并合并

    这篇文章主要为大家详细介绍了Java如何使用itext5实现生成多个PDF并合并,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-04-04
  • Java排序算法中的快速排序算法实现

    Java排序算法中的快速排序算法实现

    这篇文章主要介绍了Java排序算法中的快速排序算法实现,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,需要的朋友可以参考下
    2023-12-12
  • Java实现mybatis批量插入数据到Oracle

    Java实现mybatis批量插入数据到Oracle

    这篇文章主要为大家详细介绍了Java实现mybatis批量插入数据到Oracle 的相关资料,需要的朋友可以参考下
    2016-06-06
  • Spring Boot配置AOP打印日志的全过程

    Spring Boot配置AOP打印日志的全过程

    这篇文章主要给大家介绍了关于Spring Boot配置AOP打印日志的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08

最新评论