零 版本
- 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 办法
- 该办法是一个浅拷贝,读取和写入的对象必须雷同或者有父子继承关系(也能够是接口)
- 该办法不会抛错,只会疏忽有问题的属性