使用copyProperties方法递归处理对象

背景:

有两个相同的class对象source,target。

合并逻辑:用source中!=null的字段覆盖target中为null的字段,对于嵌套的对象需要递归处理。

比如有两个对象:(Student)s1和s2,需要将s2中的内容合并到s1中。

@Data
public Class Person{
    private String name;
    private Integer age;
}
@Data
public Class Student{
     private String schoolAddress;
     private Person personInfo;
}

Student s1;
Student s2;
思路:

基于反射去实现这一需求。spring提供了该方法copyProperties

target对象为主体,将source对象的值赋给target。默认是直接覆盖相同字段的全部值,如果不需要处理其中的某些字段,则需要将字段name传入ignoreProperties

private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
            @Nullable String... ignoreProperties) throws BeansException 

但问题在于是copyProperties只处理当前class的成员,对于内部深层对象不是逐层处理的。

比如:

我想将s1的schoolAddress:"asdfdasdf"和age:12 复制到s2中,对于copyProperties方法来说,只能将s1中的personInfo整体复制过去,而不对内部的age判断进行处理

s1:
{
    schoolAddress:"asdfdasdf",
    personInfo:{
        name:"test1",
        age:12
    }
}

s1:
{
    schoolAddress:null,
    personInfo:{
        name:"test2",
        age:null
    }
}

期望的合并结果
{
    schoolAddress:"asdfdasdf",
    personInfo:{
        name:"test2",
        age:12
    }
}

因此需要我们自己递归实现。

/**
     * 将source中内容合并到target中
     * 合并逻辑:用source中!=null的字段覆盖target中为null的字段,对于不是枚举,基础类型,包装类,Collection,String的对象进行递归处理
     * @param source
     * @param target
     * @param a
     * @param <A>
     * @throws BeansException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static <A> void updateProperties(Object source, Object target, Class<A> a) throws BeansException, InstantiationException, IllegalAccessException {
        if (source == null) {
            return;
        }
        if (target==null){
            target = a.newInstance();
        }
        final BeanWrapperImpl targetSrc = new BeanWrapperImpl(target);
        java.beans.PropertyDescriptor[] targetPds = targetSrc.getPropertyDescriptors();
        final BeanWrapperImpl sourceSrc = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] sourcePds = sourceSrc.getPropertyDescriptors();
        // target中不为null的字段不需要处理
        Set<String> hasValuePropertyNames = new HashSet<>();
        for(java.beans.PropertyDescriptor pd : targetPds) {
            Object tarValue = targetSrc.getPropertyValue(pd.getName());
            Object sourcValue = sourceSrc.getPropertyValue(pd.getName());
            // 如果为枚举,基础类型,包装类,list,String 且不为空则不需要复制,添加到名单中
            if (null != tarValue && noNeedRecursiveType(pd.getPropertyType())) {
                hasValuePropertyNames.add(pd.getName());
            } else if (tarValue==null && noNeedRecursiveType(pd.getPropertyType())){
                continue;
            } else if (pd.getPropertyType() != Class.class){
                // 其余类型对象递归处理
                if (tarValue==null){
                    tarValue = pd.getPropertyType().newInstance();
                }
                updateProperties(sourcValue, tarValue, pd.getPropertyType());
                targetSrc.setPropertyValue(pd.getName(), tarValue);
                hasValuePropertyNames.add(pd.getName());
            }
        }
        String[] result = new String[hasValuePropertyNames.size()];
        // 调用spring的copyProperties进行复制
        copyProperties(source, target, hasValuePropertyNames.toArray(result));
    }
    public static boolean noNeedRecursiveType(Class clazz){
        if (clazz==null){
            return true;
        }
        return ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() || isCollectionField(clazz) || String.class.isAssignableFrom(clazz);
    }

 private static boolean isCollectionField(Class clazz) {
        return Collection.class.isAssignableFrom(clazz);
    }

 private static boolean isPrimitiveOrWrapper(Class clazz) {
        return ClassUtils.isPrimitiveOrWrapper(clazz) || String.class.isAssignableFrom(clazz);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值