零 版本
- spring-beans 5.3.7
一 copyProperties
copyProperties(…) 在 org.springframework.beans.BeanUtils 下,在失常利用中,开发者如果须要转换 bean,通常应用到的办法是:
// originBean 是原型
// targetBean 是要转换的指标
BeanUtils.copyProperties(originBean, targetBean);
在 BeanUtils 中,copyProperties 有一系列的重载办法,然而最初都会落到一个具体的实现上:
/**
* source - origin bean
* target - 指标 bean
* editable - 这个 class 对象用于设置对 target 的抽象层次
* ignoreProperties - 要疏忽的参数
**/
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
// 判空,两个次要的 java 对象是不可为空的
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
/**
* 获取 target 的 class 对象,如果设置了泛型,则默认应用泛型
* 如果 editable 是 null,则此处疏忽
* 通常状况下是 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;
}
// 此处获取 target class 中的所有属性的形容
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
// 是否有须要疏忽的属性
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
// 此处轮询所有的属性
for (PropertyDescriptor targetPd : targetPds) {// 获取写入办法,一般来说即为 setXX(...)
Method writeMethod = targetPd.getWriteMethod();
// 确认此属性并不被疏忽,且存在写入办法
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {// 此处获取 source 中的对应属性的读取办法,一般来说即为 getXX(...)
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
// 获取 readMethod 的返回值类型
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
// 获取 writeMethod 的第一个入参类型
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
// 此处判断 readMethod 的返回类型和 writeMethod 的入参类型是否为继承关系
// 只有当这两者为继承关系,或者相等的状况下,才会进行注入
boolean isAssignable =
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
targetResolvableType.isAssignableFrom(sourceResolvableType));
if (isAssignable) {
try {
// 放开读取办法的权限
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {readMethod.setAccessible(true);
}
// 通过反射获取值
Object value = readMethod.invoke(source);
// 放开写入办法的权限
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);
}
}
}
}
}
}
}
二 总结
- 要应用该办法,必须有 get/set 办法
- 该办法是一个浅拷贝,读取和写入的对象必须雷同或者有父子继承关系(也能够是接口)
- 该办法不会抛错,只会疏忽有问题的属性