JAVA 使用反射比较对象属性的变化,记录修改日志。使用注解【策略模式】,来进行不同属性枚举值到中英文描述的切换,支持前端国际化。

1.首先定义一个接口,接口中有两个方法,分别是将属性转换成英文描述和中文描述。

其实就是将数据库中记录的 0 1 ,转换成后面的描述

这边定义了中文转换为默认方法,是因为有些属性不需要进行中文转换,或者该属性的枚举值中没有中文描述,你也可以不定义为默认方法

java 复制代码
public interface ColumnConverter {
    /**
     * 英文值
     */
    Object enConverter(Object value);

    /**
     * 中文值
     */
    default Object cnConverter(Object value) {
        return enConverter(value);
    }
}

2.然后我们就可以定义一个类,该类中有很多静态类【不同的静态类用于不同属性的枚举值转换】,代码如下

java 复制代码
@Slf4j
public class ColumnStrategy {
    private final Map<Class<? extends ColumnConverter>, ColumnConverter> converterMap = new HashMap<>();

    private ColumnStrategy() {
    }

    /**
     * 获取单例
     */
    public static ColumnStrategy getInstance() {
        return INSTANCE.Instance;
    }

    public ColumnConverter getConverter(Class<? extends ColumnConverter> converterClass) {
        try {
            if (converterMap.containsKey(converterClass)) {
                return converterMap.get(converterClass);
            }
            Constructor<? extends ColumnConverter> constructor = converterClass.getConstructor();
            ColumnConverter columnConverter = constructor.newInstance();
            converterMap.put(converterClass, columnConverter);
            return columnConverter;
        } catch (Exception e) {
            log.error("构造转换器对象异常", e);
            return null;
        }
    }

    public ColumnConverter getDefaultConverter() {
        ColumnConverter defaultConverter = converterMap.get(AutoConverter.class);
        if (defaultConverter != null) {
            return defaultConverter;
        }
        AutoConverter autoConverter = new AutoConverter();
        converterMap.put(AutoConverter.class, autoConverter);
        return autoConverter;
    }

    private static class INSTANCE {
        private static final ColumnStrategy Instance = new ColumnStrategy();
    }

    /**
     * 默认转换器
     */
    public static class AutoConverter implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return value;
        }
    }

    

    
    public static class CarrierLevelType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> CarrierTypeEnum.getByCode((int) e))
                    .map(CarrierTypeEnum::getEnDesc)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> CarrierTypeEnum.getByCode((int) e))
                    .map(CarrierTypeEnum::getDescription)
                    .orElse(null);
        }
    }


    /**
     * 结果集转换器
     * 将<转换为&lt; (前端要求)
     */
    public static class OperationDescResultConverter implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return ((String) value).replaceAll(KeyboardSpecialCharConstants.LESS, KeyboardSpecialCharConstants.LESS_ESCAPING);
        }
    }


    public static class BusinessStatusType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> BusinessStatusEnum.getByCode((int) e))
                    .map(BusinessStatusEnum::getValue)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> BusinessStatusEnum.getByCode((int) e))
                    .map(BusinessStatusEnum::getCnValue)
                    .orElse(null);
        }
    }






    public static class PriceCheckType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> PriceCheckModeEnum.getEnumByCode((int) e))
                    .map(PriceCheckModeEnum::getModeDescEn)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> PriceCheckModeEnum.getEnumByCode((int) e))
                    .map(PriceCheckModeEnum::getModeDesc)
                    .orElse(null);
        }
    }



    public static class RealPriceCalType implements ColumnConverter {
        @Override
        public Object enConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> RealPriceCalMethodEnum.getEnumByCode((int) e))
                    .map(RealPriceCalMethodEnum::getMethodDescEn)
                    .orElse(null);
        }

        @Override
        public Object cnConverter(Object value) {
            return Optional.ofNullable(value)
                    .map(e -> RealPriceCalMethodEnum.getEnumByCode((int) e))
                    .map(RealPriceCalMethodEnum::getMethodDesc)
                    .orElse(null);
        }
    }



}

3.然后我们定义一个注解,该注解用于我们实际进行比较的类中上,具体值是否需要进行枚举值转换,以及字段的中英文名称

java 复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnInfo {
    /**
     * 字段中文名
     */
    String cnName() default "";

    /**
     * 字段英文名
     */
    String enName() default "";

    /**
     * 值转换器(英文)
     * 适用于枚举型转换
     */
    Class<? extends ColumnConverter> converter() default ColumnStrategy.AutoConverter.class;

    /**
     * 是否是用户数组
     */
    boolean isUserList() default false;
}

4.然后我们就可以在实际需要进行比较的类上加上该注解【需要进行枚举值转换的属性,我们可以在属性上面的注解中加上converter ,然后注入对应的转换器即可】,示例代码如下

java 复制代码
@Data
public class DTO {


    /**
     * 业务名称
     */
    @ColumnInfo(cnName = "业务名称", enName = "Business name")
    private String bizCode;



    /**
     * 国别
     */
    @ColumnInfo(cnName = "国家", enName = "Country")
    private String country;



    /**
     * 国别
     */
    @ColumnInfo(cnName = "到期时间", enName = "Expire time")
    private String expireTime;


    /**
     * 业务状态
     */
    @ColumnInfo(cnName = "业务状态", enName = "status", converter = ColumnStrategy.BusinessStatusType.class)
    private Integer status;



    /**
     * 是否校验价格
     */
    @ColumnInfo(cnName = "是否校验价格", enName = "Is check price", converter = ColumnStrategy.PriceCheckType.class)
    private Integer priceCheckMode;



}

5.现在就可以直接传入修改前后的两个对象,利用反射对其进行修改值的检测了

java 复制代码
//调用示例
getChangeFields(DTO.class, from, to, descCnList, descEnList);    


//具体方法代码如下
private void getChangeFields(Class clazz, Object obj1, Object
            obj2, ArrayList<String> cnList, ArrayList<String> enList) {
        try {
            // 解析对象1和对象2的JSONObject
            JSONObject object1 = JSONUtil.parseObj(obj1);
            JSONObject object2 = JSONUtil.parseObj(obj2);
            if (object1.isEmpty() || object2.isEmpty()) {
                return;
            }
            // 获取该类的所有属性
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                // 设置属性可访问
                field.setAccessible(true);
                // 获取属性名
                String name = field.getName();
                ColumnInfo targetColumnInfo = field.getAnnotation(ColumnInfo.class);
                ColumnConverter columnConverter = getColumnConverter(targetColumnInfo);
                // 判断对象1和对象2的属性数量是否不为0(如果是创建则object2为null)
                // 判断对象1和对象2的属性值是否都不为空(由于有一些属性始终为null,所以需要过滤掉,不然会空指针异常)
                Object o1 = object1.get(name);
                Object o2 = object2.get(name);
                // 判断对象1和对象2的属性值是否不相等
                if (ObjectUtil.equals(o1, o2)) {
                    continue;
                }
                if (Constants.CHECK_CONVERT_FILED.contains(name)) {
                    Object cnFrom = ObjectUtil.isNotEmpty(o1) ? columnConverter.cnConverter(o1) : StringPool.EMPTY;
                    Object cnTo = ObjectUtil.isNotEmpty(o2) ? columnConverter.cnConverter(o2) : StringPool.EMPTY;
                    Object enFrom = ObjectUtil.isNotEmpty(o1) ? columnConverter.enConverter(o1) : StringPool.EMPTY;
                    Object enTo = ObjectUtil.isNotEmpty(o2) ? columnConverter.enConverter(o2) : StringPool.EMPTY;
                    // 添加属性名到列表中
                    cnList.add(String.format("%s【%s】更新为【%s】", targetColumnInfo.cnName(), cnFrom, cnTo));
                    enList.add(String.format("%s【%s】 changed to 【%s】", targetColumnInfo.enName(), enFrom, enTo));
                } else {
                    o1 = ObjectUtil.isNotEmpty(o1) ? o1 : StringPool.EMPTY;
                    o2 = ObjectUtil.isNotEmpty(o2) ? o2 : StringPool.EMPTY;
                    if (ObjectUtil.isNotEmpty(targetColumnInfo.cnName())) {
                        cnList.add(String.format("%s【%s】更新为【%s】", targetColumnInfo.cnName(), o1, o2));
                    }
                    if (ObjectUtil.isNotEmpty(targetColumnInfo.enName())) {
                        enList.add(String.format("%s【%s】 changed to 【%s】", targetColumnInfo.enName(), o1, o2));
                    }
                }

            }
        } catch (Exception e) {
            // 获取异常信息详情
            log.error("---Failed to check attributes---", e);
        }
    }
相关推荐
Tester_孙大壮9 分钟前
第4章:Python TDD消除重复与降低依赖实践
开发语言·驱动开发·python
努力搬砖的程序媛儿1 小时前
uniapp悬浮可拖拽按钮
java·前端·uni-app
上海拔俗网络1 小时前
“AI开放式目标检测系统:开启智能识别新时代
java·团队开发
数据小小爬虫1 小时前
如何使用Python爬虫获取微店商品详情:代码示例与实践指南
开发语言·爬虫·python
Leaf吧1 小时前
springboot 配置多数据源以及动态切换数据源
java·数据库·spring boot·后端
代码驿站5202 小时前
JavaScript语言的软件工程
开发语言·后端·golang
java1234_小锋2 小时前
Java中如何安全地停止线程?
java·开发语言
siy23332 小时前
[c语言日寄]结构体的使用及其拓展
c语言·开发语言·笔记·学习·算法
栗子~~2 小时前
基于quartz,刷新定时器的cron表达式
java
杨过姑父2 小时前
Servlet3 简单测试
java·servlet