零 版本

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