一、属性无法复制
- target 必须是 editable 的子类。
- source 属性的 get 方法必须是 public 才能复制。
- target 属性没有 set 方法的无法复制。
- target 的 set 方法不是 public 无法复制。
- 对于某一个相同属性名的属性: 如果 source 和 target 不同类型,无法复制; source 和 target 类型相同,可以复制; source extends target 可以复制。
二、复制流程分析
1、判断 target 是否是 editable 的子类。如果不是就报错,是继续下面的逻辑。
2、取出 target 的所有属性。
3、对 target 的每一个属性进行遍历。
3.1、拿到 target 属性的 set 方法。排除不需要复制的属性。拿不到,返回。
3.2、取出 source 属性的 set 和 get 方法。
3.3、判断 target 的 set 的类是否能转化为 source 的 get 类。无法转换,返回。
3.4、判断 source 的 get 方法是否是 public。
3.5、使用反射调用 source 的 get 方法,获取到值。
3.6、判断 target 的 set 方法是否是 public 修饰。
3.7、使用反射调用 target 的 set 方法,设置值。
4、复制属性成功,进行下一个属性的复制。
三、核心代码分析
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,@Nullable 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) {// 判断 target 是否是 editable 的子类。if (!editable.isInstance(target)) {throw new IllegalArgumentException("Target class [" + target.getClass().getName() +"] not assignable to Editable class [" + editable.getName() + "]");}actualEditable = editable;}// 读取 editable/target(editable>target) 的所有的属性。PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);// 排除某些不需要复制的属性。List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);// 对 target 的每一个属性进行遍历。for (PropertyDescriptor targetPd : targetPds) {// 拿到 target 属性的写方法(set方法)。Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {// 拿到相应的属性修饰器。PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {// 拿到 source 的 get 方法。Method readMethod = sourcePd.getReadMethod();// 首先 source 属性是有 get 方法,target 的 set 的类是否能转化为 source 的 get 类。if (readMethod != null &&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {try {// 判断 source 的 get 方法是否是 public。if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {readMethod.setAccessible(true);}// 使用反射调用 source 的 get 方法,获取到值。Object value = readMethod.invoke(source);// 判断 target 的 set 方法是否是 public 修饰。if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {writeMethod.setAccessible(true);}// 使用反射调用 target 的 set 方法,设置值。writeMethod.invoke(target, value);}catch (Throwable ex) {throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);}}}}}}