场景
在 Java 项目中,经常遇到需要在对象之间拷贝属性的问题。然而,除了直接使用 Getter/Stter 方法,我们还有其他的方法么?
当然有,例如 Apache Common Lang3 的 BeanUtils,然而 BeanUtils 却无法完全满足吾辈的需求,所以吾辈便自己封装了一个,这里分享出来以供参考。
- 需要大量复制对象的属性
- 对象之间的属性名可能是不同的
- 对象之间的属性类型可能是不同的
目标
简单易用的 API
- copy: 指定需要拷贝的源对象和目标对象
- prop: 拷贝指定对象的字段
- props: 拷贝指定对象的多个字段
- exec: 执行真正的拷贝操作
- from: 重新开始添加其他对象的属性
- get: 返回当前的目标对象
- config: 配置拷贝的一些策略
思路
- 定义门面类 BeanCopyUtil 用以暴露出一些 API
- 定义每个字段的操作类 BeanCopyField,保存对每个字段的操作
- 定义 BeanCopyConfig,用于配置拷贝属性的策略
- 定义 BeanCopyOperator 作为拷贝的真正实现
图解
实现
注:反射部分依赖于 joor, JDK1.8 请使用 joor-java-8
定义门面类 BeanCopyUtil 用以暴露出一些 API
public class BeanCopyUtil{ private final F from; private final T to; private final List copyFieldList = new linkedList<>(); private BeanCopyConfig config = new BeanCopyConfig(); private BeanCopyUtil(F from, T to) { this.from = from; this.to = to; } public static BeanCopyUtil copy(F from, T to) { return new BeanCopyUtil<>(from, to); } public BeanCopyUtil prop(String fromField, String toField, Function super Object, ? super Object> converter) { copyFieldList.add(new BeanCopyField(fromField, toField, converter)); return this; } public BeanCopyUtil prop(String fromField, String toField) { return prop(fromField, toField, null); } public BeanCopyUtil prop(String field, Function super Object, ? super Object> converter) { return prop(field, field, converter); } public BeanCopyUtil prop(String field) { return prop(field, field, null); } public BeanCopyUtil props(String... fields) { for (String field : fields) { prop(field); } return this; } public BeanCopyUtil exec() { new BeanCopyOperator<>(from, to, copyFieldList, config).copy(); return this; } public BeanCopyUtil from(R from) { return new BeanCopyUtil<>(from, to); } public T get() { return to; } public BeanCopyUtil config(BeanCopyConfig config) { this.config = config; return this; } }
定义每个字段的操作类 BeanCopyField,保存对每个字段的操作
public class BeanCopyField {
private String from;
private String to;
private Function super Object, ? super Object> converter;
public BeanCopyField() {
}
public BeanCopyField(String from, String to, Function super Object, ? super Object> converter) {
this.from = from;
this.to = to;
this.converter = converter;
}
public String getFrom() {
return from;
}
public BeanCopyField setFrom(String from) {
this.from = from;
return this;
}
public String getTo() {
return to;
}
public BeanCopyField setTo(String to) {
this.to = to;
return this;
}
public Function super Object, ? super Object> getConverter() {
return converter;
}
public BeanCopyField setConverter(Function super Object, ? super Object> converter) {
this.converter = converter;
return this;
}
}
定义 BeanCopyConfig,用于配置拷贝属性的策略
public class BeanCopyConfig {
private boolean same = true;
private boolean override = true;
private boolean ignoreNull = true;
private boolean converter = true;
public BeanCopyConfig() {
}
public BeanCopyConfig(boolean same, boolean override, boolean ignoreNull, boolean converter) {
this.same = same;
this.override = override;
this.ignoreNull = ignoreNull;
this.converter = converter;
}
public boolean isSame() {
return same;
}
public BeanCopyConfig setSame(boolean same) {
this.same = same;
return this;
}
public boolean isOverride() {
return override;
}
public BeanCopyConfig setOverride(boolean override) {
this.override = override;
return this;
}
public boolean isIgnoreNull() {
return ignoreNull;
}
public BeanCopyConfig setIgnoreNull(boolean ignoreNull) {
this.ignoreNull = ignoreNull;
return this;
}
public boolean isConverter() {
return converter;
}
public BeanCopyConfig setConverter(boolean converter) {
this.converter = converter;
return this;
}
}
定义 BeanCopyOperator 作为拷贝的真正实现
public class BeanCopyOperator{ private static final Logger log = LoggerFactory.getLogger(BeanCopyUtil.class); private final F from; private final T to; private final BeanCopyConfig config; private List copyFieldList; public BeanCopyOperator(F from, T to, List copyFieldList, BeanCopyConfig config) { this.from = from; this.to = to; this.copyFieldList = copyFieldList; this.config = config; } public void copy() { //获取到两个对象所有的属性 final Map fromFields = Reflect.on(from).fields(); final Reflect to = Reflect.on(this.to); final Map toFields = to.fields(); //过滤出所有相同字段名的字段并进行拷贝 if (config.isSame()) { final Map > different = ListUtil.different(new ArrayList<>(fromFields.keySet()), new ArrayList<>(toFields.keySet())); copyFieldList = Stream.concat(different.get(ListUtil.ListDiffState.common).stream() .map(s -> new BeanCopyField(s, s, null)), copyFieldList.stream()) .collect(Collectors.toList()); } //根据拷贝字段列表进行拷贝 copyFieldList.stream() //忽略空值 .filter(beanCopyField -> !config.isIgnoreNull() || fromFields.get(beanCopyField.getFrom()).get() != null) //覆盖属性 .filter(beanCopyField -> config.isOverride() || toFields.get(beanCopyField.getTo()).get() == null) //如果没有转换器,则使用默认的转换器 .peek(beanCopyField -> { if (beanCopyField.getConverter() == null) { beanCopyField.setConverter(Function.identity()); } }) .forEach(beanCopyField -> { final String fromField = beanCopyField.getFrom(); final F from = fromFields.get(fromField).get(); final String toField = beanCopyField.getTo(); try { to.set(toField, beanCopyField.getConverter().apply(from)); } catch (ReflectException e) { log.warn("Copy field failed, from {} to {}, exception is {}", fromField, toField, e.getMessage()); } }); } }
使用
使用流程图
测试
代码写完了,让我们测试一下!
public class BeanCopyUtilTest {
private final Logger log = LoggerFactory.getLogger(getClass());
private Student student;
private Teacher teacher;
@Before
public void before() {
student = new Student("琉璃", 10, "女", 4);
teacher = new Teacher();
}
@Test
public void copy() {
//简单的复制(类似于 BeanUtils.copyProperties)
BeanCopyUtil.copy(student, teacher).exec();
log.info("teacher: {}", teacher);
assertThat(teacher)
.extracting("age")
.containsOnlyOnce(student.getAge());
}
@Test
public void prop() {
//不同名字的属性
BeanCopyUtil.copy(student, teacher)
.prop("sex", "sex", sex -> Objects.equals(sex, "男"))
.prop("realname", "name")
.exec();
assertThat(teacher)
.extracting("name", "age", "sex")
.containsOnlyOnce(student.getRealname(), student.getAge(), false);
}
@Test
public void prop1() {
//不存的属性
assertThat(BeanCopyUtil.copy(student, teacher)
.prop("sex", "sex", sex -> Objects.equals(sex, "男"))
.prop("realname", "name2")
.exec()
.get())
.extracting("age", "sex")
.containsOnlyOnce(student.getAge(), false);
}
@Test
public void from() {
final Teacher lingMeng = new Teacher()
.setName("灵梦")
.setAge(17);
//测试 from 是否覆盖
assertThat(BeanCopyUtil.copy(student, teacher)
.prop("sex", "sex", sex -> Objects.equals(sex, "男"))
.prop("realname", "name")
.exec()
.from(lingMeng)
.exec()
.get())
.extracting("name", "age", "sex")
.containsOnlyOnce(lingMeng.getName(), lingMeng.getAge(), false);
}
@Test
public void get() {
//测试 get 是否有效
assertThat(BeanCopyUtil.copy(student, teacher)
.prop("sex", "sex", sex -> Objects.equals(sex, "男"))
.prop("realname", "name")
.exec()
.get())
.extracting("name", "age", "sex")
.containsOnlyOnce(student.getRealname(), student.getAge(), false);
}
@Test
public void config() {
//不自动复制同名属性
assertThat(BeanCopyUtil.copy(new Student().setAge(15), new Teacher())
.config(new BeanCopyConfig().setSame(false))
.exec()
.get())
.extracting("age")
.containsOnlyNulls();
//不覆盖不为空的属性
assertThat(BeanCopyUtil.copy(new Student().setAge(15), new Teacher().setAge(10))
.config(new BeanCopyConfig().setOverride(false))
.exec()
.get())
.extracting("age")
.containsOnlyOnce(10);
//不忽略源对象不为空的属性
assertThat(BeanCopyUtil.copy(new Student(), student)
.config(new BeanCopyConfig().setIgnoreNull(false))
.exec()
.get())
.extracting("realname", "age", "sex", "grade")
.containsOnlyNulls();
}
private static class Student {
private String realname;
private Integer age;
private String sex;
private Integer grade;
public Student() {
}
public Student(String realname, Integer age, String sex, Integer grade) {
this.realname = realname;
this.age = age;
this.sex = sex;
this.grade = grade;
}
public String getRealname() {
return realname;
}
public Student setRealname(String realname) {
this.realname = realname;
return this;
}
public Integer getAge() {
return age;
}
public Student setAge(Integer age) {
this.age = age;
return this;
}
public String getSex() {
return sex;
}
public Student setSex(String sex) {
this.sex = sex;
return this;
}
public Integer getGrade() {
return grade;
}
public Student setGrade(Integer grade) {
this.grade = grade;
return this;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
private static class Teacher {
private String name;
private Integer age;
private Boolean sex;
private String post;
public Teacher() {
}
public Teacher(String name, Integer age, Boolean sex, String post) {
this.name = name;
this.age = age;
this.sex = sex;
this.post = post;
}
public String getName() {
return name;
}
public Teacher setName(String name) {
this.name = name;
return this;
}
public Integer getAge() {
return age;
}
public Teacher setAge(Integer age) {
this.age = age;
return this;
}
public Boolean getSex() {
return sex;
}
public Teacher setSex(Boolean sex) {
this.sex = sex;
return this;
}
public String getPost() {
return post;
}
public Teacher setPost(String post) {
this.post = post;
return this;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}
如果没有发生什么意外,那么一切将能够正常运行!
好了,那么关于在 Java 中优雅的拷贝对象属性就到这里啦
以上就是Java 如何优雅的拷贝对象属性的详细内容,更多关于Java 拷贝对象属性的资料请关注考高分网其它相关文章!



