java中属性复制工具类实现

介绍

在做开发的过程中经常会遇到属性的复制,如果手动逐个进行复制会很麻烦,所以需要一个工具类来简化开发。主要考虑三种情况:
1、Pojo -> Pojo
2、List<Pojo> -> List<Pojo>
3、Map -> Pojo

下面介绍两种工具类的编写,一个是借助spring框架的BeanUtils,另一个是自己封装。

借助BeanUtils完成工具类

借助spring的BeanUtils有限制,只能在spring环境下使用,好处就是工具类很好编写,目前大多数项目都是使用spring,所以实用性还是很不错的
下面看代码:

import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
 * 属性复制工具类,有几点要求:
 * 1、如果目标对象不是实例,则目标对象要有无参构造器
 * 2、源对象要有getter方法,目标对象要有setter方法,并且属性类型一致或源对象属性是目标对象属性的子类
 * 3、字段的泛型类型要一致或源对象是目标对象的子类型,否则取值时会报错{@link ClassCastException}
 */
public class CopyPropertiesUtil {
    /*私有化构造器*/
    private CopyPropertiesUtil() {
    }
	//两个pojo对象之间属性的复制
    public static <T> T copyProperties(Object source, T target) {
        validate(source, target);
        BeanUtils.copyProperties(source, target);
        return target;
    }
	//通过无参构造器实例化对象,进行属性复制
    public static <T> T copyProperties(Object source, Class<T> target) {
        T t = getInstance(target);
        return copyProperties(source, t);
    }
	//复制集合中元素属性,使用无参构造器实例化对象
    public static <T> List<T> copyListProperties(List<?> source, Class<T> target) {
        Objects.requireNonNull(source, "source can not be null!");
        return source.stream().map(sou -> copyProperties(sou, target)).collect(Collectors.toList());
    }
	//复制Map中的属性,key是属性名称,value是属性值
    public static <T> T copyMapProperties(Map<String, ?> map, T target) {
    	validate(map,target);
        return copyMapProperties(map, target, target.getClass());
    }
	//复制Map中的属性,key是属性名称,value是属性值,使用无参构造器实例化对象
    public static <T> T copyMapProperties(Map<String, ?> map, Class<T> target) {
    	validate(map,target);
        T t = getInstance(target);
        return copyMapProperties(map, t, target);
    }
	
    @SuppressWarnings("unchecked")
    private static <T> T copyMapProperties(Map<String, ?> map, Object target, Class<?> targetClazz) {
    	//获取Class对象中所有的属性描述符
        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(targetClazz);
        //遍历进行属性复制
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            Method writeMethod = propertyDescriptor.getWriteMethod();
            //写方法为空,进行下一个属性复制(即setter方法)
            if (writeMethod == null) {
                continue;
            }
            //获取属性的类型
            Class<?> propertyType = propertyDescriptor.getPropertyType();
            //获取属性的名称
            String name = propertyDescriptor.getName();
            //获取属性对应的值
            Object o = map.get(name);
            //如果属性值不为空,并且属性值对象可以赋值给目标属性
            if (o != null && propertyType.isAssignableFrom(o.getClass())) {
                try {
                	//设置写方法可执行
                    if (!writeMethod.isAccessible()) {
                        writeMethod.setAccessible(true);
                    }
                    writeMethod.invoke(target, o);
                } catch (Exception e) {
                    throw new RuntimeException("invoke method " + writeMethod.getName() + " failed!", e);
                }
            }
        }
        return (T) target;
    }
	//无参构造器实例化对象
    private static <T> T getInstance(Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            if (!constructor.isAccessible()) {
                constructor.setAccessible(true);
            }
            return constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(clazz.getName() + " must have non arg constructor!", e);
        }
    }
	//验证,源对象和目标对象都不能为空
    private static void validate(Object source, Object target) {
        Objects.requireNonNull(source, "source can not be null!");
        Objects.requireNonNull(target, "target can not be null!");
    }
}

解释下 “属性类型一致或源对象属性是目标对象属性的子类”
“属性类型一致”很好理解,例如,Integer可以赋值给Integer
“源对象属性是目标对象属性的子类”的意思是,从源对象中获取的属性值得类型是目标对象属性值类型的子类型,例如:源对象是:Integer,目标对象是:Number,Integer可以赋值给Number
当然,一般情况下,源对象和目标对象的属性类型都是一致的

自己手动封装,不借助框架的工具类

自己手动封装的好处就是在任何框架下都能使用,缺点就是工具类的编写比较麻烦。
首先认识一个与属性相关的类PropertyDescriptor,它是在java.beans包下,属于jdk自带的,下面看看这个类中常用的方法:

1、java.beans.PropertyDescriptor#PropertyDescriptor(String,Class<?>)

PropertyDescriptor的构造器,第一个参数是属性名称,第二个参数是属性所在类的Class对象

2、java.beans.FeatureDescriptor#getName()

父类中的方法,获取属性名称

3、java.beans.PropertyDescriptor#getReadMethod()

获取读方法,即getter方法

4、java.beans.PropertyDescriptor#getWriteMethod()

获取写方法,即setter方法

5、java.beans.PropertyDescriptor#getPropertyType()

获取属性的Class类型

PropertyDescriptor的源码还是比较简单的,有兴趣的可以看看

自定义反射器获取类的所有属性的PropertyDescriptor对象

下面是具体的实现代码:

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 属性查找规则:只找字段对应的属性(针对pojo对象,适用大部分情况,已经够用了)
 * 更具体规则,查找getter和setter方法对应的属性
 */
public class Reflector {
    private Class<?> clazz;
    //存放属性和对应的属性描述符对象
    private Map<String, PropertyDescriptor> descriptorMap;

    public Reflector(Class<?> clazz) {
        Objects.requireNonNull(clazz, "class can not be null!");
        this.clazz = clazz;
        descriptorMap = new HashMap<>();
        init();
    }

    //查看是否含有对应的属性
    public boolean containsProperty(String name) {
        return descriptorMap.containsKey(name);
    }

    //返回属性描述的不可变的Map对象,防止无意识的修改
    public Map<String, PropertyDescriptor> getDescriptorMap() {
        return Collections.unmodifiableMap(descriptorMap);
    }

    //初始化descriptorMap集合
    private void init() {
        Class<?> currentClass = clazz;
        //遍历类的继承体系
        while (currentClass != Object.class) {
            //只查找字段对应的属性
            Field[] fields = currentClass.getDeclaredFields();
            for (Field field : fields) {
                String name = field.getName();
                try {
                    PropertyDescriptor descriptor = new PropertyDescriptor(name, currentClass);
                    descriptorMap.put(name, descriptor);
                } catch (IntrospectionException e) {
                    throw new RuntimeException("can not get " + name + " descriptor", e);
                }
            }
            currentClass = currentClass.getSuperclass();
        }
    }
}

改造CopyPropertiesUtil工具类

看代码:

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 属性复制工具类,有几点要求:
 * 1、如果目标对象不是实例,则目标对象要有无参构造器
 * 2、源对象要有getter方法,目标对象要有setter方法,并且属性类型一致或源对象属性是目标对象属性的子类
 * 3、字段的泛型类型要一致或源对象是目标对象的子类型,否则取值时会报错{@link ClassCastException}
 */
public class CopyPropertiesUtil {
    //缓存
    private static Map<Class<?>, Reflector> reflectorMap = new HashMap<>();
    /*
     * 锁对象,当然可以使用ConcurrentHashMap#putIfAbsent,但是在并发情况下可能会出现两次初始化Reflector对象
     * */
    /*
     *
     * private static Reflector getReflector(Class<?> clazz){
     *    ConcurrentHashMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
     *    Reflector reflector = reflectorMap.get(clazz);
     *    if (reflector == null) {
     *        当两个线程同时执行到此处时,会初始化两次Reflector对象,虽然没什么影响,但使用lock更好一点
     *        reflector = new Reflector(clazz);
     *        reflectorMap.putIfAbsent(clazz, reflector);
     *    }
     *    return reflector;
     *}
     * */
    private static final Object lock = new Object();

    /*私有化构造器*/
    private CopyPropertiesUtil() {
    }

    //根据指定的Class对象获取Reflector对象
    private static Reflector getReflector(Class<?> clazz) {
        Reflector reflector = reflectorMap.get(clazz);
        if (reflector == null) {
            synchronized (lock) {
                reflector = reflectorMap.get(clazz);
                if (reflector == null) {
                    reflector = new Reflector(clazz);
                    reflectorMap.put(clazz, reflector);
                }
            }
        }
        return reflector;
    }

    //两个pojo对象之间属性的复制
    public static <T> T copyProperties(Object source, T target) {
        validate(source, target);
        copyProperties(source, target, target.getClass());
        return target;
    }

    //通过无参构造器实例化对象,进行属性复制
    public static <T> T copyProperties(Object source, Class<T> target) {
        validate(source, target);
        T t = getInstance(target);
        copyProperties(source, t, target);
        return t;
    }


    private static void copyProperties(Object source, Object target, Class<?> targetClass) {
        //获取源对象的ReflectorSupport
        ReflectorSupport sourceReflectorSupport = getReflectorSupport(source.getClass(), source);
        //获取目标对象的ReflectorSupport
        ReflectorSupport targetReflectorSupport = getReflectorSupport(targetClass, target);
        //属性复制
        copyProperties(sourceReflectorSupport, targetReflectorSupport);
    }

    private static void copyProperties(ReflectorSupport sourceReflectorSupport, ReflectorSupport targetReflectorSupport) {
        Map<String, PropertyDescriptor> sourceDescriptorMap = sourceReflectorSupport.getDescriptorMap();
        Map<String, PropertyDescriptor> targetDescriptorMap = targetReflectorSupport.getDescriptorMap();
        //遍历源对象的属性描述符map对象
        sourceDescriptorMap.forEach((name, sourceDescriptor) -> {
            //获取目标对象的属性描述符
            PropertyDescriptor targetDescriptor = targetDescriptorMap.get(name);
            //为空则继续下一个
            if (targetDescriptor == null) {
                return;
            }
            //获取目标对象的写方法(setter方法)
            Method writeMethod = targetDescriptor.getWriteMethod();
            //获取源对象的读方法(getter方法)
            Method readMethod = sourceDescriptor.getReadMethod();
            //有一个为空,则继续下一个
            if (writeMethod == null || readMethod == null) {
                return;
            }
            //获取目标对象属性的Class对象
            Class<?> targetPropertyType = targetDescriptor.getPropertyType();
            //获取源对象属性的Class对象
            Class<?> sourcePropertyType = sourceDescriptor.getPropertyType();
            //源对象属性的要可赋值给目标对象属性
            if (targetPropertyType.isAssignableFrom(sourcePropertyType)) {
                if (!writeMethod.isAccessible()) {
                    writeMethod.setAccessible(true);
                }
                if (!readMethod.isAccessible()) {
                    readMethod.setAccessible(true);
                }
                try {
                    writeMethod.invoke(targetReflectorSupport.object, readMethod.invoke(sourceReflectorSupport.object));
                } catch (Exception e) {
                    throw new RuntimeException("method invoke failed and property is " + name, e);
                }
            }
        });
    }

    private static ReflectorSupport getReflectorSupport(Class<?> clazz, Object object) {
        Reflector reflector = getReflector(clazz);
        return new ReflectorSupport(reflector, object);
    }

    //内部辅助类
    private static class ReflectorSupport {
        Reflector reflector;
        Object object;

        public ReflectorSupport(Reflector reflector, Object object) {
            this.reflector = reflector;
            this.object = object;
        }

        public Map<String, PropertyDescriptor> getDescriptorMap() {
            return reflector.getDescriptorMap();
        }
    }

    //复制集合中元素属性,使用无参构造器实例化对象
    public static <T> List<T> copyListProperties(List<?> source, Class<T> target) {
        Objects.requireNonNull(source, "source can not be null!");
        return source.stream().map(sou -> copyProperties(sou, target)).collect(Collectors.toList());
    }

    //复制Map中的属性,key是属性名称,value是属性值
    public static <T> T copyMapProperties(Map<String, ?> map, T target) {
        validate(map, target);
        return copyMapProperties(map, target, target.getClass());
    }

    //复制Map中的属性,key是属性名称,value是属性值,使用无参构造器实例化对象
    public static <T> T copyMapProperties(Map<String, ?> map, Class<T> target) {
        validate(map, target);
        T t = getInstance(target);
        return copyMapProperties(map, t, target);
    }

    @SuppressWarnings("unchecked")
    private static <T> T copyMapProperties(Map<String, ?> map, Object target, Class<?> targetClazz) {
        //获取Class的Reflector对象
        Reflector reflector = getReflector(targetClazz);
        //获取所有的属性描述符
        Map<String, PropertyDescriptor> descriptorMap = reflector.getDescriptorMap();
        //遍历map对属性进行赋值
        map.forEach((name, value) -> {
            //根据属性名称获取属性的描述符对象
            PropertyDescriptor propertyDescriptor = descriptorMap.get(name);
            //为空则继续下一个
            if (propertyDescriptor == null) {
                return;
            }
            //获取写方法(setter方法)
            Method writeMethod = propertyDescriptor.getWriteMethod();
            //为空则继续下一个
            if (writeMethod == null) {
                return;
            }
            //获取属性的类型
            Class<?> propertyType = propertyDescriptor.getPropertyType();
            //如果属性值不为空,并且属性值对象可以赋值给目标属性
            if (value != null && propertyType.isAssignableFrom(value.getClass())) {
                //设置方法可执行
                if (!writeMethod.isAccessible()) {
                    writeMethod.setAccessible(true);
                }
                try {
                    writeMethod.invoke(target, value);
                } catch (Exception e) {
                    throw new RuntimeException("method invoke failed and property is " + name, e);
                }
            }
        });
        return (T) target;
    }

    //无参构造器实例化对象
    private static <T> T getInstance(Class<T> clazz) {
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            if (!constructor.isAccessible()) {
                constructor.setAccessible(true);
            }
            return constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(clazz.getName() + " must have non arg constructor!", e);
        }
    }

    //验证,源对象和目标对象都不能为空
    private static void validate(Object source, Object target) {
        Objects.requireNonNull(source, "source can not be null!");
        Objects.requireNonNull(target, "target can not be null!");
    }
}

改造过后的工具类,可以在摆脱框架的束缚。

总结

没有考虑过多的业务场景,但是已经可以适用大部分情况了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值