Spring的BeanUtils.copyProperties方法

  • 用途

    BeanUtils的copyProperties方法用来做属性copy。
    使用时的注意点:

    1. target和source的属性名类型和名称必须相同
    2. 名称大小写敏感
    3. 如果名称大小写不一致,或者类型不一致,则跳过,不做属性复制

  • 原理

    copyProperties使用了jdk自带的自省机制自省 简单来说就是jdk在 反射 上又做了一层包装,针对于Bean对象的属性读写。

    大概逻辑如下:

    1. 遍历target对象的属性
    2. 获取target对象属性的write方法
    3. 根据target对象的属性名,获取source对象的属性
    4. 如果有同样名字的属性,调用source对象属性的read方法得到值
    5. 调用target对象属性的write方法,给属性赋值

    主要源码如下:

    public abstract class BeanUtils {
    
        //省略其余代码
    
        public static void copyProperties(Object source, Object target) throws BeansException {
            copyProperties(source, target, null, (String[]) null);
        }
    
        public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
            copyProperties(source, target, editable, (String[]) null);
        }
    
        public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
            copyProperties(source, target, null, ignoreProperties);
        }
    
        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) {
                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);
    
            //循环target的属性列表
            for (PropertyDescriptor targetPd : targetPds) {
                //获取写方法
                Method writeMethod = targetPd.getWriteMethod();
                if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                    //根据target的属性名,获取source对应的属性
                    PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                    if (sourcePd != null) {
                        //获取source属性的读方法
                        Method readMethod = sourcePd.getReadMethod();
                        //校验写方法的参数类型和读方法的返回类型是否一致,如果不一致则跳过
                        if (readMethod != null &&
                                ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                            try {
                                //如果读方法不是public的,则设置accessible=true
                                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                    readMethod.setAccessible(true);
                                }
                                Object value = readMethod.invoke(source);
                                //如果写方法不是public的,则设置accessible=true
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }
                                //target.setXXX(source.getXXX())
                                writeMethod.invoke(target, value);
                            }
                            catch (Throwable ex) {
                                throw new FatalBeanException(
                                        "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                            }
                        }
                    }
                }
            }
        }
    
        public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
            CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
            return cr.getPropertyDescriptors();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值