工具类:获取比较两个对象不同的属性名和值,改进 BeanUtils.copyProperties()方法可以指定字段名不一样进行复制

/**
 * 比较两个实体属性值,返回一个map以有差异的属性名为key,value为一个Map分别存oldObject,newObject此属性名的值
 * @param oldObject 进行属性比较的对象1
 * @param newObject 进行属性比较的对象2
 * @return 属性差异比较结果map
 */
public static Map<String, Map<String,Object>> compareFields(Object oldObject, Object newObject) {}
/**
 * 合并两个map中的元素
 * @param map1 Map<String, Map<String,Object>>
 * @param map2 Map<String, Map<String,Object>>
 * @return 合并后的Map<String, Map<String,Object>>
 */
public static Map<String, Map<String,Object>> mergeMap(Map<String, Map<String,Object>> map1, Map<String, Map<String,Object>> map2) {}
/**
 * 可以给定一个map key存储源对象的字段值,value存储目标对象的字段,可以实现两个不同字段名称间的属性复制
 * @param source 源对象
 * @param target 目标对象
 * @param map 字段映射map
 */
public static void copyProperties(Object source, Object target,Map<String,String> map) throws BeansException {}
import com.sun.istack.internal.Nullable;
import org.springframework.beans.*;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.*;

/**
 * 类工具类
 * @author : Xiewy <br/>
 * @date : 2022/5/30 14:35 <br/>
 */
@Component
public class CheckBeanUtils {

    /**
     * 比较两个实体属性值,返回一个map以有差异的属性名为key,value为一个Map分别存oldObject,newObject此属性名的值
     * @param oldObject 进行属性比较的对象1
     * @param newObject 进行属性比较的对象2
     * @return 属性差异比较结果map
     */
    public static Map<String, Map<String,Object>> compareFields(Object oldObject, Object newObject) {
        Map<String, Map<String, Object>> map = null;

        try{
            /**
             * 只有两个对象都是同一类型的才有可比性
             */
            if (oldObject.getClass() == newObject.getClass()) {
                map = new HashMap<String, Map<String,Object>>();
                Class clazz = oldObject.getClass();
                //获取object的所有属性
                PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz,Object.class).getPropertyDescriptors();
                for (PropertyDescriptor pd : pds) {
                    //遍历获取属性名
                    String name = pd.getName();
                    //获取属性的get方法
                    Method readMethod = pd.getReadMethod();
                    // 在oldObject上调用get方法等同于获得oldObject的属性值
                    Object oldValue = readMethod.invoke(oldObject);
                    // 在newObject上调用get方法等同于获得newObject的属性值
                    Object newValue = readMethod.invoke(newObject);

                    if(oldValue instanceof List){
                        continue;
                    }
                    if(newValue instanceof List){
                        continue;
                    }
                    if(oldValue instanceof Timestamp){
                        oldValue = new Date(((Timestamp) oldValue).getTime());
                    }
                    if(newValue instanceof Timestamp){
                        newValue = new Date(((Timestamp) newValue).getTime());
                    }
                    if(oldValue == null && newValue == null){
                        continue;
                    }else if(oldValue == null && newValue != null){
                        Map<String,Object> valueMap = new HashMap<String,Object>();
                        valueMap.put("oldValue",oldValue);
                        valueMap.put("newValue",newValue);
                        map.put(name, valueMap);
                        continue;
                    }
                    if (!oldValue.equals(newValue)) {// 比较这两个值是否相等,不等就可以放入map了
                        Map<String,Object> valueMap = new HashMap<String,Object>();
                        valueMap.put("oldValue",oldValue);
                        valueMap.put("newValue",newValue);
                        map.put(name, valueMap);
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 合并两个map中的元素
     * @param map1 Map<String, Map<String,Object>>
     * @param map2 Map<String, Map<String,Object>>
     * @return 合并后的Map<String, Map<String,Object>>
     */
    public static Map<String, Map<String,Object>> mergeMap(Map<String, Map<String,Object>> map1, Map<String, Map<String,Object>> map2) {
        Map<String, Map<String,Object>> map = new HashMap<>();
        if (map1 == null && map2 == null){
            return null;
        }
        if (map1 == null){
            return map2;
        }
        if (map2 == null){
            return map1;
        }
        map.putAll(map1);
        map.putAll(map2);
        return map;
    }

    /**
     *  BeanUtils.copyProperties,深层copy是原数据为null不进行覆盖
     * @param source 资源类
     * @return
     */
    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set emptyNames = new HashSet();
        for(java.beans.PropertyDescriptor pd : pds) {
            //检查此属性的值是否为 null 然后将其添加到集合中
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return (String[]) emptyNames.toArray(result);
    }

    /**
     * 功能描述: 属性复制
     * @param source 源对象
     * @param target 目标对象
     * @param ignoreProperties 忽略的属性
     */
    public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
        copyProperties(source, target, null,null, ignoreProperties);
    }

    /**
     * 功能描述: 属性复制
     */
    public static void copyProperties(Object source, Object target) throws BeansException {
        copyProperties(source, target, null,null, (String[]) null);
    }

    /**
     * 功能描述: 属性复制
     * @param source 源对象
     * @param target 目标对象
     * @param map 字段映射map
     */
    public static void copyProperties(Object source, Object target,Map<String,String> map) throws BeansException {
        copyProperties(source, target, null,map, (String[]) null);
    }

    /**
     * 拓展springBeanUtils
     * 可以给定一个map key存储源对象的字段值,value存储目标对象的字段,可以实现两个不同字段名称间的属性复制
     *
     * @param source 源对象(bean)
     * @param target 目标对象(bean)
     * @param editable  将属性设置限制为的类(或接口)
     * @param map 字段映射map   key存储源对象的字段值,value存储目标对象的字段
     * @param ignoreProperties 要忽略的属性名称数组
     * @throws BeansException 如果复制失败
     * @see BeanWrapper
     */
    private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
                                       @Nullable Map<String, String> map, @Nullable String... ignoreProperties) throws BeansException {

        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        // 拿到目标对象字节码对象
        Class<?> actualEditable = source.getClass();
        if (editable != null) {
            if (!editable.isInstance(source)) {
                throw new IllegalArgumentException("Source class [" + source.getClass().getName() +
                        "] not assignable to Editable class [" + editable.getName() + "]");
            }
            actualEditable = editable;
        }
        // PropertyDescriptor描述Java Bean中通过一对存储器方法(getter / setter)导出的一个属性。我们可以通过该PropertyDescriptor对bean中的该属性进行读取和写入操作,也可以设置其getter / setter。
        PropertyDescriptor[] sourcePds = BeanUtils.getPropertyDescriptors(actualEditable);
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

        // 遍历目标对象的属性
        for (PropertyDescriptor sourcePd : sourcePds) {
            Method readMethod = sourcePd.getReadMethod();
            if (readMethod != null && (ignoreList == null || !ignoreList.contains(sourcePd.getName()))) {
                PropertyDescriptor targetPd = null;
                // 如果map有,则取map中的字段
                if (map != null && map.get(sourcePd.getName()) != null && !"".equals(map.get(sourcePd.getName()))) {
                    targetPd = BeanUtils.getPropertyDescriptor(target.getClass(), map.get(sourcePd.getName()));
                   
                } else {
                    // 如果map没有,则找相同名称的字段
                    targetPd = BeanUtils.getPropertyDescriptor(target.getClass(), sourcePd.getName());
                }
                if (targetPd != null) {
                    Method writeMethod = targetPd.getWriteMethod();
                    if (writeMethod != null &&
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        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);
                        }
                    }
                }
            }
        }
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值