对象属性复制BeanCopier-笔记
1. BeanCopier简介
org.springframework.cglib.beans.BeanCopier
是 Spring 框架中用于高效复制 Java Bean 属性的工具类。它基于 CGLIB 库实现,相比传统的 BeanUtils.copyProperties()
方法,具有更高的性能,尤其在需要频繁复制对象属性的场景中表现更优。
功能特点
- 高性能
使用字节码生成技术(CGLIB)动态生成复制代码,避免了反射的性能损耗。 - 简单属性复制
仅复制属性名和类型相同的字段,不支持复杂映射(如嵌套对象、自定义转换逻辑)。 - 依赖 CGLIB
需要 CGLIB 库支持,Spring 已内置该依赖(如spring-core
依赖中已包含 CGLIB)。
与 Spring BeanUtils 的区别
特性 | BeanCopier | BeanUtils.copyProperties() |
---|---|---|
性能 | 更快(字节码生成) | 较慢(基于反射) |
依赖 | CGLIB | Spring 或 Apache Commons BeanUtils |
复杂映射支持 | 不支持 | 不支持 |
构造函数要求 | 目标类需有无参构造函数 | 同上 |
2. 使用示例
2.1 常见用法
通常,源、目标对象的属性的名称、类型都一样,无需做额外转换,可直接使用 BeanCopier
做属性复制。
step1. 定义源对象和目标对象
public class User {private String name;private int age;// Getters and Setterspublic String getName() { return name; }public void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }
}public class UserDTO {private String name;private int age;// Getters and Setterspublic String getName() { return name; }public void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; }
}
step2. 使用 BeanCopier
进行属性复制
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;public class BeanCopierExample {public static void main(String[] args) {// 创建 BeanCopier 实例(第三个参数表示是否使用 Converter,这里User和UserDTO对象的属性字段一致,无需做转换)BeanCopier beanCopier = BeanCopier.create(User.class, UserDTO.class, false);// 源对象和目标对象User user = new User();user.setName("Alice");user.setAge(30);UserDTO userDTO = new UserDTO();// 执行属性复制(第三个参数表示转换所需的Converter,这里无需转换,该参数设置为null即可)beanCopier.copy(user, userDTO, null);System.out.println("Name: " + userDTO.getName()); // 输出: AliceSystem.out.println("Age: " + userDTO.getAge()); // 输出: 30}
}
注意事项
- 目标类必须有无参构造函数:
BeanCopier
通过构造函数创建目标对象。 - 属性名和类型必须一致:若类型不匹配,会抛出异常。
- 不处理嵌套对象:仅复制简单属性(如
String
、int
等)。
适用场景
- 高频复制简单 Java Bean 属性(如 DTO 转 VO)。
- 对性能敏感的场景(如大数据量导出、缓存填充等)。
2.2 需做属性类型转换
当源、目标对象的属性名相同但类型不一致时,可以通过实现 Converter
接口来支持源对象与目标对象之间属性类型的转换。例如将 Date
类型转换为 String
类型,或者将某种业务对象转换为另一种表示形式。
通过传入自定义的 Converter
实现,可以实现:
- 自定义类型转换逻辑
- 支持字段级别的转换控制
- 保持高性能复制的同时处理复杂类型
step1.定义源对象和目标对象,注意 birthDate 字段属性的区别
import java.util.Date;//源类
public class User {private String name;private Date birthDate;public String getName() { return name; }public void setName(String name) { this.name = name; }public Date getBirthDate() { return birthDate; }public void setBirthDate(Date birthDate) { this.birthDate = birthDate; }
}//目标类
public class UserDTO {private String name;private String birthDate; // 注意这里是 String 类型public String getName() { return name; }public void setName(String name) { this.name = name; }public String getBirthDate() { return birthDate; }public void setBirthDate(String birthDate) { this.birthDate = birthDate; }
}
step2.实现 Converter
接口
我们实现一个 Converter
来将 Date
转换为 String
(格式化为 yyyy-MM-dd
):
import org.springframework.cglib.core.Converter;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateToStringConverter implements Converter {private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");@Overridepublic Object convert(Object source, Class targetClass, Object context) {if (source instanceof Date) {return sdf.format((Date) source);}return source; // 不处理其他类型}
}
convert
方法说明:
source
:源对象中的属性值targetClass
:目标属性的类型context
:属性名(String)
step3. 使用 BeanCopier
+ Converter
完成复制
import org.springframework.cglib.beans.BeanCopier;public class BeanCopierWithConverterExample {public static void main(String[] args) {// 创建 BeanCopier 实例,启用 ConverterBeanCopier beanCopier = BeanCopier.create(User.class, UserDTO.class, true);// 构造源对象User user = new User();user.setName("Alice");user.setBirthDate(new Date());// 构造目标对象UserDTO userDTO = new UserDTO();// 执行复制,传入 Converter 实例beanCopier.copy(user, userDTO, new DateToStringConverter());System.out.println("Name: " + userDTO.getName());System.out.println("Birth Date: " + userDTO.getBirthDate()); // 输出格式如 2025-04-05}
}
使用 Converter
的注意事项
-
必须设置
useConverter = true
在调用BeanCopier.create()
时,第三个参数必须设为true
,否则Converter
不会被使用。 -
Converter 是无状态的
建议每次复制使用一个新的Converter
实例,或确保其实现是线程安全的。 -
仅转换特定字段
你可以通过context
参数(即属性名)判断只对某些字段进行转换。 -
不处理嵌套对象
BeanCopier
不会递归复制嵌套对象,Converter 也仅用于简单属性转换。
Converter的
适用场景
- 源对象和目标对象字段名相同但类型不同(如
Date
↔String
) - 需要自定义格式化或简化对象结构
- 性能要求较高,但映射关系简单