【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!
博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起进步!
吾等采石之人,应怀大教堂之心,愿我们奔赴在各自的热爱里…
一、案例学习
在实际开发中,有时候我们需要将一个Java Bean的属性值复制到另一个Java Bean中。但是有一些情况下,我们不想复制所有属性,而是需要忽略某些属性。例如,两个Java Bean的属性名称不一致,我们需要忽略属性名称不一致的属性。这时候,就可以使用BeanUtils.copyProperties方法的复制忽略指定属性实现。
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
@Data
public class Student {
private String name;
private int age;
private String gender;
// getters and setters...
public static void main(String[] args) throws Exception {
ArrayList<Student> students = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Student student = new Student();
student.setName("Student " + i);
student.setAge(i * 10);
student.setGender("Male");
students.add(student);
}
// 使用 BeanUtils.copyProperties 复制学生信息,但忽略性别属性
ArrayList<Student> copiedStudents = new ArrayList<>();
for (Student student : students) {
Student copiedStudent = new Student();
String[] ignoredProperties = {"gender"}; // 指定忽略的属性
BeanUtils.copyProperties(student, copiedStudent, ignoredProperties);
copiedStudents.add(copiedStudent);
}
System.out.println("Original Students:");
for (Student student : students) {
System.out.println(student.getName() + ", Age: " + student.getAge()
+ ", Gender: " + student.getGender() );
}
System.out.println("Copied Students:");
for (Student student : copiedStudents) {
System.out.println(student.getName() + ", Age: " + student.getAge()
+ ", Gender: " + student.getGender() );
}
}
}
执行结果
Original Students:
Student 0, Age: 0, Gender: Male
Student 1, Age: 10, Gender: Male
Student 2, Age: 20, Gender: Male
Copied Students:
Student 0, Age: 0, Gender: null
Student 1, Age: 10, Gender: null
Student 2, Age: 20, Gender: null
对比
二、源码学习
BeanUtils.copyProperties()是Spring Framework中的一个工具类,用于将一个JavaBean中的属性值复制到另一个JavaBean中。它的源码如下:
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, (String[])null);
}
public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
copyProperties(source, target, editable, (String[])null);
}
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
copyProperties(source, target, null, ignoreProperties);
}
public static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
for (PropertyDescriptor targetPd : targetPds) {
if (targetPd.getWriteMethod() == null || (ignoreList != null && ignoreList.contains(targetPd.getName()))) {
continue;
}
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null && sourcePd.getReadMethod() != null) {
try {
Method readMethod = sourcePd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
Method writeMethod = targetPd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
该方法的实现逻辑如下:
- 首先进行参数校验,source和target参数不能为空;
- 如果editable参数不为空,则如果target不是该editable的实例,则抛出异常;
- 如果editable参数为空,那么就将target.getClass()的值赋给actualEditable;
- 获取目标对象(target)的PropertyDescriptor数组;
- 如果ignoreProperties不为空,则将其转换为List对象,否则赋值为null;
- 对于每个PropertyDescriptor对象,判断其是否有WriteMethod方法,以及是否在ignoreProperties中,如果符合条件,则跳过;
- 获取源对象(source)中与目标对象中相同属性名的PropertyDescriptor对象;
- 如果sourcePd不为空,且sourcePd有ReadMethod方法,则执行以下操作:
- 获取sourcePd中的方法对象(Method);
- 如果该方法的修饰符不是public,则将其设置为可访问的;
- 调用该方法获取到其返回值(value);
- 获取targetPd中的方法对象(Method);
- 如果该方法的修饰符不是public,则将其设置为可访问的;
- 调用该方法,将value设置为其参数值;
- 如果发生异常,则抛出FatalBeanException异常。
总体来说,该方法的实现逻辑比较清晰,先进行参数校验,然后对目标对象的PropertyDescriptor数组进行遍历,在每次遍历时,将源对象中与目标对象中属性名相同的PropertyDescriptor对象的ReadMethod方法获取到,然后调用它获取到其返回值,最后再调用目标对象的PropertyDescriptor对象的WriteMethod方法,将值设置为其参数值。
📖☕️🌊📝📚🎩🚀
📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤️ 分享👥 留言💬thanks!!!
📚愿我们奔赴在各自的热爱里!