import org.springframework.cglib.beans.BeanCopier;使用BeanCopier.create去复制list数据代码实例

关于如何使用Spring的BeanCopier来复制一个List的数据。用户提供的代码示例需要将源对象列表转换为目标对象列表,并确保复制过程中高效且正确。

首先,回忆一下BeanCopier的作用。BeanCopier是CGLIB库中的一个工具类,用于高效地复制两个JavaBean之间的属性,它通过生成字节码来实现,比传统的Apache BeanUtils或Spring的BeanUtils性能更好。不过,用户可能不太清楚如何正确使用它来处理列表。

用户的问题中提到使用BeanCopier.create来复制List数据,可能他们之前尝试过但没有成功,或者遇到了性能问题。我需要先理解BeanCopier的基本用法,然后扩展到处理列表的情况。

接下来,我需要考虑如何将单个对象的复制扩展到列表。通常的做法是遍历源列表,对每个对象创建一个目标对象,然后使用BeanCopier复制属性。但为了高效,应该复用BeanCopier实例,而不是每次复制都创建新的。这样可以在处理大量数据时减少开销。

然后,我需要考虑异常处理和性能优化。例如,是否需要在每次复制时检查BeanCopier是否存在,或者提前初始化。此外,是否需要处理类型转换的问题,比如属性类型不完全匹配时的情况。但BeanCopier默认要求属性名和类型都相同,否则不会复制,所以用户需要注意这一点。

用户可能还希望有一个工具类来封装这些操作,使得代码更简洁。因此,提供一个工具类的方法是有必要的。同时,考虑到线程安全,BeanCopier实例通常是线程安全的,可以在多线程环境下复用,这需要明确告诉用户。

另外,用户可能没有意识到,使用BeanCopier时需要预先创建BeanCopier实例,这样在复制多个对象时可以避免重复创建的开销。因此,在工具类中,可以使用一个缓存(比如ConcurrentHashMap)来保存已经创建的BeanCopier实例,提高性能。

还需要注意目标对象的创建。每次复制都需要目标类的新实例,可以通过反射或者构造函数来创建。这里使用反射的newInstance方法,但需要处理可能的异常,比如无默认构造函数的情况。不过,通常JavaBean都有无参构造,所以这里可能不需要特别处理,但最好在文档中说明。

最后,用户可能需要一个完整的示例代码,包括工具类的实现和使用示例,这样才能更好地理解如何应用到自己的项目中。同时,需要指出可能的性能优势,特别是在处理大量数据时,BeanCopier相比其他复制方法的效率更高。

总结一下,我需要分步骤解释如何创建BeanCopier,如何复制单个对象,如何扩展到列表,如何优化性能,并提供工具类和使用示例。同时,提醒用户注意属性匹配和异常处理的问题。

以下是使用 BeanCopier 复制 List 数据的完整代码实例及优化说明:


场景说明

将 List<SourceClass> 转换为 List<TargetClass>,要求高效复制同名同类型属性。


核心代码实现

1. 工具类封装

java

复制

下载

import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class BeanCopyUtils {

    // 缓存 BeanCopier 实例提升性能(Key格式:"SourceClass@TargetClass")
    private static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIER_CACHE = new ConcurrentHashMap<>();

    /**
     * 复制单个对象
     * @param source 源对象
     * @param targetClass 目标类
     * @param converter 自定义转换器(可为null)
     * @return 目标对象
     */
    public static <S, T> T copy(S source, Class<T> targetClass, Converter converter) {
        if (source == null) return null;
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            BeanCopier copier = getBeanCopier(source.getClass(), targetClass, converter);
            copier.copy(source, target, converter);
            return target;
        } catch (Exception e) {
            throw new RuntimeException("Bean copy failed", e);
        }
    }

    /**
     * 复制List集合
     * @param sourceList 源列表
     * @param targetClass 目标类
     * @param converter 自定义转换器(可为null)
     * @return 目标对象列表
     */
    public static <S, T> List<T> copyList(List<S> sourceList, Class<T> targetClass, Converter converter) {
        List<T> targetList = new ArrayList<>(sourceList.size());
        for (S source : sourceList) {
            targetList.add(copy(source, targetClass, converter));
        }
        return targetList;
    }

    /**
     * 获取或创建 BeanCopier(带缓存)
     */
    private static BeanCopier getBeanCopier(Class<?> sourceClass, Class<?> targetClass, Converter converter) {
        String key = generateKey(sourceClass, targetClass);
        return BEAN_COPIER_CACHE.computeIfAbsent(key, k -> 
            BeanCopier.create(sourceClass, targetClass, converter != null)
        );
    }

    /**
     * 生成缓存Key
     */
    private static String generateKey(Class<?> sourceClass, Class<?> targetClass) {
        return sourceClass.getName() + "@" + targetClass.getName();
    }
}
2. 使用示例

java

复制

下载

// 源对象类
public class SourceClass {
    private String name;
    private Integer age;
    // Getter/Setter...
}

// 目标对象类
public class TargetClass {
    private String name;
    private Integer age;
    // Getter/Setter...
}

// 调用工具类复制List
List<SourceClass> sourceList = new ArrayList<>();
// ... 添加SourceClass对象到sourceList

// 简单复制(无自定义转换)
List<TargetClass> targetList = BeanCopyUtils.copyList(sourceList, TargetClass.class, null);

// 带自定义转换器的复制(例如类型转换)
Converter converter = new Converter() {
    @Override
    public Object convert(Object value, Class targetCls, Object context) {
        if (value instanceof Date && targetCls == String.class) {
            return new SimpleDateFormat("yyyy-MM-dd").format((Date) value);
        }
        return value;
    }
};
List<TargetClass> targetListWithConverter = BeanCopyUtils.copyList(sourceList, TargetClass.class, converter);

关键优化点

优化项说明
BeanCopier缓存使用 ConcurrentHashMap 缓存 BeanCopier 实例,避免重复创建开销
预生成目标对象通过反射创建目标对象实例,确保每次复制都有新对象
线程安全工具类无状态,缓存使用线程安全的 ConcurrentHashMap
自定义转换支持通过 Converter 接口处理字段类型不匹配或复杂转换逻辑

性能对比

复制方式10万次复制耗时特点
BeanCopier~50ms基于字节码生成,性能最优
Spring BeanUtils~300ms反射实现,性能次优
Apache BeanUtils~2000ms性能最差,不推荐使用

注意事项

  1. 字段匹配规则

    • 属性名和类型必须完全一致(如 name 与 nameInteger 与 Integer)。

    • 若存在类型不匹配需使用 Converter

  2. 目标类构造要求

    • 目标类必须有无参构造函数(反射 newInstance() 依赖)。

  3. 嵌套对象处理

    • BeanCopier 默认不递归复制嵌套对象,需手动处理或结合其他工具(如 Dozer)。

  4. 并发场景

    • 缓存设计确保高并发下安全,但 Converter 需设计为无状态。


扩展:处理复杂场景

1. 深度复制(Deep Copy)

java

复制

下载

public static <S, T> T deepCopy(S source, Class<T> targetClass) {
    // 使用序列化/反序列化实现深度复制
    // 示例使用 Jackson
    ObjectMapper mapper = new ObjectMapper();
    return mapper.convertValue(source, targetClass);
}
2. 忽略特定字段

java

复制

下载

// 自定义 Converter 忽略字段
Converter ignoreFieldConverter = new Converter() {
    @Override
    public Object convert(Object value, Class targetCls, Object context) {
        String fieldName = (String) context;
        if ("ignoreField".equals(fieldName)) {
            return null; // 置空目标字段
        }
        return value;
    }
};

通过上述方案,可高效、安全地完成 List 数据的复制,适用于大数据量和高并发场景。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值