关于java:Spring-之-BeanUtilscopyProperties-源码简读

38次阅读

共计 2658 个字符,预计需要花费 7 分钟才能阅读完成。

零 版本

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

正文完
 0