CopyUtil(java复合bean的copy)

诞生由来

之前项目里就经常有bean之间的属性copy,一直都是用spring自带的工具类来解决,方法如下:

org.springframework.beans.BeanUtils#copyProperties(java.lang.Object, java.lang.Object);

该方法只能对一个bean中普通属性字段进行copy,如果这个bean中有其他的bean,即bean中bean,就需要将其值取出,单独对这个子bean进行copy,然后set进父bean,这种copy如果在bean层次少的时候还无所谓,如果在bean的层次比较深的时候,就会发现很是繁琐了,最近遇到的项目里面有bean和bo的转换,其深度就有四五层,所以如果能想出一个新的工具类,能够一次性copy其bean中bena肯定最好不过了。

以前用的简单包装工具类

  • 满足单层bean的copy。
  • 满足单层bean集合的copy。
public class CopyUtil {
    @SuppressWarnings("all")
    public static <T> T copy(Object fromBean, Class<T> clz)
        throws InstantiationException, IllegalAccessException {
        if (fromBean == null || clz == null) {
            return null;
        }
        T result = clz.newInstance();
        BeanUtils.copyProperties(fromBean, result);
        return result;
    }

    public static <T> List<T> copy(Collection<?> fromBeans, Class<T> cls)
        throws InstantiationException, IllegalAccessException {
        if (CollectionUtils.isEmpty(fromBeans) || null == cls) {
            return null;
        }
        List<T> list = new ArrayList<>();
        for (Object fromBean : fromBeans) {
            T t = copy(fromBean, cls);
            if (t != null) {
                list.add(t);
            }
        }
        return list;
    }
}

通过反射写出的新的一套工具类

  • 满足单层bean的copy。
  • 满足单层bean集合的copy。
  • 满足多层bean的复合copy。
思路
  • 获取fromBean中的属性字段,如果toBean中也有其属性字段,则继续下一步。
  • 反射执行get/is获取其属性field,得到属性类型type,属性值fieldValue。
  • 如果type是普通类型,则直接反射执行set方法将值写入toBean。
  • 如果type是List集合类型,调用getFieldIfList获取对应toBean中的该属性值toList:
    • 获取这个List类型属性里面包裹的属性类型type_child。
    • 如果type_child仍是List集合类型,递归调用getFieldIfList去掉List外壳,并将值add进其父List。
    • 如果type_child不是集合类型,通过Class.forName获取其class:
      • 如果type_child是一般类型,直接将传进来的list值add进toList返回。
      • 如果type_child是child_bean类型,遍历传进来的list,对其每个值又重新调用copy方法set进new出来的新child_bean实例中,返回。
  • 如果type是Map类型,暂不考虑。
  • 如果type不是以上类型(忽略考虑了java里面或者其他类里面定义的内置bean类型),判定为bean中bean类型:
    • 反射获取toBean中对应字段的class:
      Class<?> newToClass = toClass.getDeclaredField(field.getName()).getType();
      Object newToBean = newToClass.newInstance();
      
    • 再次调用copy方法将fieldValue写入newToBean,然后执行set方法将newToBean写入toBean。
  • 最终返回toBean即目标结果。
判断是否是普通字段属性
/**
* @Author ifdream
* @Description 返回该字段的类型是否是普通类型
* @Date 10:14 2019/9/21
* @Param [clazz]
* @return boolean
**/
private static boolean isNormalField(Class clazz) {
    return clazz == int.class || clazz == String.class || clazz == float.class
        || clazz == double.class || clazz == char.class || clazz == byte.class
        || clazz == short.class || clazz == Date.class || clazz == boolean.class
        || clazz == Integer.class || clazz == Float.class
        || clazz == Double.class || clazz == Character.class || clazz == Byte.class
        || clazz == Short.class || clazz == Boolean.class
        || clazz == long.class || clazz == Long.class;
}
整体工具类
/**
 * @Auther: ifdream
 * @Date: 2019/9/21 09:39
 * @Description:
 */
public class CopyUtil {

    private final static String LIST_STR = "java.util.List";
    private final static String SERIAL_FIELD_STR = "serialVersionUID";
    private final static int LIST_STR_LEN = "java.util.List".length();

    @SuppressWarnings("all")
    public static <T> T copy(Object fromBean, Class<T> clz)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException,
            NoSuchFieldException, ClassNotFoundException, InstantiationException {
        if (fromBean == null || clz == null) {
            return null;
        }
        if (isNormalField(clz)) {
            if (fromBean.getClass() == clz) {
                // 类型一致
                return (T) fromBean;
            }
            throw new IllegalArgumentException("this fromBean is not " + clz + " type");
        }
        return copy(fromBean, clz.newInstance());
    }

    public static <T> List<T> copy(Collection<?> fromBeans, Class<T> cls)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException,
            NoSuchFieldException, ClassNotFoundException, InstantiationException {
        if (CollectionUtils.isEmpty(fromBeans) || null == cls) {
            return null;
        }
        List<T> list = new ArrayList<>();
        for (Object fromBean : fromBeans) {
            T t = copy(fromBean, cls);
            if (t != null) {
                list.add(t);
            }
        }
        return list;
    }

    /**
     * @Author ifdream
     * @Description 要求 bean 中对应字段的属性名必须一致,普通类型字段要求字段类型一致
     * @Date 10:12 2019/9/21
     * @Param [fromBean, toBean] 从 fromBean 复制到 toBean
     * @return E
     **/
    @SuppressWarnings("all")
    public static <T, E> E copy(Object fromBean, E toBean)
            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException,
            NoSuchFieldException, ClassNotFoundException, InstantiationException {
        if (fromBean == null || toBean == null) return null;
        Class<?> fromClass = fromBean.getClass();
        Class<?> toClass = toBean.getClass();
        for (Field field : fromClass.getDeclaredFields()) {
            if (field.getName().equals(SERIAL_FIELD_STR)) {
                // 序列化字段,忽略
                continue;
            }
            try {
                if (toClass.getDeclaredField(field.getName()) == null) {
                    // toClass 中没有该属性字段
                    continue;
                }
                if (isNormalField(field.getType())
                        && toClass.getDeclaredField(field.getName()).getType() != field.getType()) {
                    // 属性是普通字段,且二者对应属性字段的类型不同
                    continue;
                }
            } catch (NoSuchFieldException e) {
                continue;
            }
            String getMethodName = "get" + StringUtils.capitalize(field.getName());
            String setMethodName = "set" + StringUtils.capitalize(field.getName());
            String isMethodName = "is" + StringUtils.capitalize(field.getName());
            Object fieldValue;
            if (field.getType() == boolean.class || field.getType() == Boolean.class) {
                // boolean 类型
                fieldValue = fromClass.getDeclaredMethod(isMethodName).invoke(fromBean);
            } else {
                fieldValue = fromClass.getDeclaredMethod(getMethodName).invoke(fromBean);
            }
            if (null == fieldValue) {
                // fromBean 无此属性值
                continue;
            }
            if (isNormalField(field.getType())) {
                Method method = toClass.getDeclaredMethod(setMethodName, field.getType());
                method.invoke(toBean, fieldValue);
            } else if (field.getType() == List.class) {
                List<T> list = (List<T>) fieldValue;
                String str = toClass.getDeclaredField(field.getName()).getGenericType().toString();
                List<Object> toList = getFieldIfList(list, str);
                Method method = toClass.getDeclaredMethod(setMethodName, List.class);
                method.invoke(toBean, toList);
            } else if (field.getType() == Map.class){
                // TODO Map 暂不处理
            } else {
                // bean 中 bean
                Class<?> newToClass = toClass.getDeclaredField(field.getName()).getType();
                Object newToBean = newToClass.newInstance();
                copy(fieldValue, newToBean);
                Method method = toClass.getDeclaredMethod(setMethodName, newToClass);
                method.invoke(toBean, newToBean);
            }
        }
        return toBean;
    }

    /**
     * @Author ifdream
     * @Description 获取当源属性类型是 {@link List} 时的目标属性值
     * @Date 12:13 2019/9/22
     * @Param [list, toBeanClassStr]
     * @return java.util.List<java.lang.Object>
     **/
    @SuppressWarnings("all")
    private static <T, E> List<Object> getFieldIfList(List<T> list, String toBeanClassStr)
            throws IllegalAccessException, InstantiationException, ClassNotFoundException,
            NoSuchMethodException, NoSuchFieldException, InvocationTargetException {
        String newBeanClassStr = toBeanClassStr.substring(LIST_STR_LEN + 1,
                toBeanClassStr.length() - 1);
        if (newBeanClassStr.contains(LIST_STR)) {
            List<Object> result = new ArrayList<>();
            for (T t : list) {
                // 递归去掉 list 外壳
                List<Object> toT = getFieldIfList((List<E>) t, newBeanClassStr);
                result.add(toT);
            }
            return result;
        }
        Class<?> toBeanClass = Class.forName(newBeanClassStr);
        List<Object> toList = new ArrayList<>();
        if (isNormalField(toBeanClass)) {
            // 说明 list 里面的元素是一般属性字段
            toList.addAll(list);
        } else {
            for (T t : list) {
                Object toT = copy(t, toBeanClass.newInstance());
                toList.add(toT);
            }
        }
        return toList;
    }

    /**
     * @Author ifdream
     * @Description 返回该字段的类型是否是普通类型
     * @Date 10:14 2019/9/21
     * @Param [clazz]
     * @return boolean
     **/
    private static boolean isNormalField(Class clazz) {
        return clazz == int.class || clazz == String.class || clazz == float.class
                || clazz == double.class || clazz == char.class || clazz == byte.class
                || clazz == short.class || clazz == Date.class || clazz == boolean.class
                || clazz == Integer.class || clazz == Float.class
                || clazz == Double.class || clazz == Character.class || clazz == Byte.class
                || clazz == Short.class || clazz == Boolean.class
                || clazz == long.class || clazz == Long.class;
    }
}
简单测试
@Data
public class Bean1 {
    private String name;
    private List<Bean1_1> list;
    private Bean1_2 testBean;
    private List<String> aList;
    private List<List<Integer>> bList;
    private List<List<Bean1_3>> cList;
}
@Data
public class Bean2 {
    private String name;
    private List<Bean2_1> list;
    private Bean2_2 testBean;
    private List<String> aList;
    private List<List<Integer>> bList;
    private List<List<Bean2_3>> cList;
}
... 里面都是一些简单的bean,就不一一展开

main方法简单set值进行copy,打印对比测试:

Bean1(name=aaa, list=[Bean1_1(name=name0, id=6, testBean=[Bean1_1_1(age=17, birthday=Sun Sep 22 14:11:47 CST 2019), Bean1_1_1(age=16, birthday=Sun Sep 22 14:11:47 CST 2019)], success=true), Bean1_1(name=name1, id=0, testBean=[Bean1_1_1(age=15, birthday=Sun Sep 22 14:11:47 CST 2019), Bean1_1_1(age=15, birthday=Sun Sep 22 14:11:47 CST 2019)], success=true)], testBean=Bean1_2(test=100), aList=[a1, a2], bList=[[1, 2], [1, 2]], cList=[[Bean1_3(id=10), Bean1_3(id=10), Bean1_3(id=10)], [Bean1_3(id=11), Bean1_3(id=11), Bean1_3(id=11)]])
Bean2(name=aaa, list=[Bean2_1(name=name0, id=6, testBean=[Bean2_1_1(age=17, birthday=Sun Sep 22 14:11:47 CST 2019), Bean2_1_1(age=16, birthday=Sun Sep 22 14:11:47 CST 2019)], success=true), Bean2_1(name=name1, id=0, testBean=[Bean2_1_1(age=15, birthday=Sun Sep 22 14:11:47 CST 2019), Bean2_1_1(age=15, birthday=Sun Sep 22 14:11:47 CST 2019)], success=true)], testBean=Bean2_2(test=100), aList=[a1, a2], bList=[[1, 2], [1, 2]], cList=[[Bean2_3(id=10), Bean2_3(id=10), Bean2_3(id=10)], [Bean2_3(id=11), Bean2_3(id=11), Bean2_3(id=11)]])

结语

以上为博主应需写出的工具类,肯定不尽完美,读者发现有问题,可以提出来,博主很乐意一直完善该工具类~

如果您看到了这里,欢迎和我沟通交流!
             一个95后码农

个人博客:fy-blog

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值