Spring 之 BeanUtils.copyProperties(...) 源码简读
Posted 三流
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 之 BeanUtils.copyProperties(...) 源码简读相关的知识,希望对你有一定的参考价值。
零 版本
- 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 方法
- 该方法是一个浅拷贝,读取和写入的对象必须相同或者有父子继承关系(也可以是接口)
- 该方法不会抛错,只会忽略有问题的属性
以上是关于Spring 之 BeanUtils.copyProperties(...) 源码简读的主要内容,如果未能解决你的问题,请参考以下文章