前言
最近公司在做优化方面的工作,其中有些服务需要重构。过程中会涉及到数据迁移、双写、补偿、补偿等,刚好一起做这块的有个刚从京东过来的大佬。
前天 Review 了 下这个大佬的代码,数据校验 自定义 Handler、数据对比定义工具类、大量使用设计模式、代码干净有条理!
经大佬同意,在此记录下他的数据对比代码,学习和分享下实现思路和严谨的代码风格!
对比工具一
使用场景:比较两个对象,如果有比较对象属性名的Map,则仅仅比较传入的,否则比较全部字段
/** * 通用比对方法 */ public class DataCompareUtil { public static List<String> simpleFieldCompare(Object a, Object b) { return simpleFieldCompare(a, b, null, false); } public static List<String> simpleFieldCompare(Object a, Object b, boolean ignoreNotExistField) { return simpleFieldCompare(a, b, null, ignoreNotExistField); } /** * 比较两个对象 * * @param a * @param b * @param condition 要比较的属性名 * @return */ public static List<String> simpleFieldCompare(Object a, Object b, Map<String, String> condition) { return simpleFieldCompare(a, b, condition, false); } /** * @param aObject * @param bObject * @param condition 放一些需要比较的字段 * @param ignoreNotExistField 是否忽略不存在的field * @return */ private static List<String> simpleFieldCompare(Object aObject, Object bObject, Map<String, String> condition, boolean ignoreNotExistField) { List<String> diffRecord = new ArrayList<>(); if (aObject == null || bObject == null) { if (aObject != bObject) { diffRecord.add(String.format("有空对象:对象A=%s, 对象B=%s", JSON.toJSONString(aObject), JSON.toJSONString(bObject))); } return diffRecord; } // 获取aObject的所有字段 Class<?> aClass = aObject.getClass(); List<Field> aFields = getAllFields(aClass); Class<?> bClass = bObject.getClass(); List<Field> bFields = getAllFields(bClass); // key是fieldName,value是对应的Field Map<String, Field> aFieldMap = aFields.stream().collect(Collectors.toMap(Field::getName, Function.identity(), ((x, y) -> x))); Map<String, Field> bFieldMap = bFields.stream().collect(Collectors.toMap(Field::getName, Function.identity(), ((x, y) -> x))); if (condition == null || condition.isEmpty()) { diffRecord.addAll(aFieldMap.entrySet().stream().map(entry -> { String aFieldName = entry.getKey(); Field aField = entry.getValue(); Field bField = bFieldMap.get(aFieldName); // 说明两个对象的属性有所差异 if (bField == null) { if (ignoreNotExistField) { return null; } return "属性【" + aFieldName + "】在 对象B 中不存在"; } // 俩对象属性名称对应,就开始比较相应的属性值 return compare(aFieldName, aFieldName, aField, bField, aObject, bObject); }).filter(Objects::nonNull).collect(Collectors.toList())); if (!ignoreNotExistField) { diffRecord.addAll(bFieldMap.keySet().stream().map(bFieldName -> { if (aFieldMap.get(bFieldName) == null) { return "属性【" + bFieldName + "】在 对象A 中不存在"; } return null; }).filter(Objects::nonNull).collect(Collectors.toList())); } } else { // 比对那些差异字段 diffRecord.addAll(condition.entrySet().stream().map(entry -> { String aFieldName = entry.getKey(); String bFieldName = entry.getValue(); Field aField = aFieldMap.get(aFieldName); Field bField = bFieldMap.get(bFieldName); if (aField == null || bField == null) { if (aField != bField) { return String.format("有空对象:对象A=%s, 对象B=%s", JSON.toJSONString(aObject), JSON.toJSONString(bObject)); } return null; } return compare(aFieldName, bFieldName, aField, bField, aObject, bObject); }).filter(Objects::nonNull).collect(Collectors.toList())); } return diffRecord; } /** * 对象属性值对比方法 * * @param aName obj1待比较的属性名称 * @param bName obj2待比较的属性名称 * @param aField obj1待比较的Field * @param bField obj2待比较的Field * @return */ private static String compare(String aName, String bName, Field aField, Field bField, Object a, Object b) { try { // Filed.get(Object obj),获取对象对应的属性名。 妈的,差点把大哥的代码改了,,,我这沙雕 Object aObject = aField.get(a); Object bObject = bField.get(b); // 判断null的逻辑 if (aObject == null || bObject == null) { // 属性值一个是null,一个不是 if (aObject != bObject) { return "属性值不一致,对象A: " + aName + " = " + JSON.toJSONString(aObject) + ", 对象B: " + bName + " = " + JSON.toJSONString(bObject); } else { // 都为null return null; } } if (!aObject.equals(bObject)) { return "属性值不一致,对象A: " + aName + " = " + JSON.toJSONString(aObject) + ", 对象B: " + bName + " = " + JSON.toJSONString(bObject); } else { return null; } } catch (IllegalAccessException e) { return "属性比较异常: 对象A name =【" + aName + "】, 对象B name = 【" + bName + "】"; } } /** * 获取当前类以及其父类所有的属性列表 * * @param clazz * @return */ private static List<Field> getAllFields(Class<?> clazz) { List<Field> fieldList = new ArrayList<>(); Field[] fields = clazz.getDeclaredFields(); do { if (fields.length > 0) { fieldList.addAll(Arrays.stream(fields).peek(field -> field.setAccessible(true)).collect(Collectors.toList())); } clazz = clazz.getSuperclass(); fields = clazz.getFields(); } while (!clazz.isInstance(Object.class)); return fieldList; } }
对比工具二
使用场景:
采用多线程异步的方式,基准值采用外部传入的方式,异步调取接口执行完封装好数据,才会有线程调用对比方法。 原值采用内部接口调用的方式
支持数据类型丰富:基本引用类型、集合、JSON字符串、Object等,且支持内部嵌套、类的继承、接口实现等
工具类的代码有条理,逻辑清晰,其中也含有递归、迭代等算法思想
/** * @Description 数据比对线程 */ @Slf4j public class CompareToolsThread implements Runnable{ private Object param; private JSONObject orderJson; private Class beanClz; private String methodName; public CompareToolsThread(Object param, JSONObject orderJson, Class clz, String methodName) { this.param = param; this.orderJson = orderJson; this.beanClz = clz; this.methodName = methodName; } @Override public void run() { log.info("CompareToolsThread run-------"); Object beanClass = SpringUtils.getBean(beanClz); JSONObject jsonResult = null; try { // 调用方法 Object object = beanClass.getClass().getDeclaredMethod(methodName, param.getClass()).invoke(beanClass, param); if (object == null) { log.info("查询接口失败,参数为:{}", JSONObject.toJSONString(param)); return; } if (object instanceof HashMap) { jsonResult = new JSONObject((Map<String, Object>) object); } else if (object instanceof JSONObject) { jsonResult = (JSONObject) object; } } catch (Exception e) { log.info("查询接口异常,参数为:{}", JSONObject.toJSONString(param)); } for (Map.Entry entry : jsonResult.entrySet()) { String key = (String) entry.getKey(); //要比对的对象 Object value = entry.getValue(); // 既然比较的是原值和基准值,那么就是拿着原值和基准值比较。不可能原值为Null,基准值存在的情况 //基准值 orderJson是异步的 Object originValue = orderJson.get(key); //都为null,继续比对 if (value == null && originValue == null) { continue; } if (value == null || originValue == null) { log.info("对比失败,key===>{}", entry.getKey()); break; } boolean result = compareValue(value, originValue); if (!result) { log.info("对比失败,key===>{}", entry.getKey()); return; } } } /** * @Description 内容对比 * @Param value,originValue * @return boolean **/ private boolean compareValue(Object value, Object originValue) { // value原值 originValue基准值 if (originValue == null && value != null) { return false; } if (originValue instanceof String) { return value.equals(originValue); } if (originValue instanceof Integer) { return value.equals(originValue); } if (originValue instanceof Long) { return value.equals(originValue); } if (originValue instanceof Double) { return value.equals(originValue); } if (originValue instanceof Float) { return value.equals(originValue); } if (originValue instanceof Byte) { return value.equals(originValue); } if (originValue instanceof Boolean) { return value.equals(originValue); } if (originValue instanceof Date) { return ((Date) value).compareTo((Date) originValue) == 0; } if (originValue instanceof BigDecimal) { return (value instanceof BigDecimal ? (BigDecimal)value : new BigDecimal(value.toString())).compareTo((BigDecimal) originValue) == 0; } //toLowerCase(Locale.ROOT)):如果有其他语言,需要这个参数,如果仅仅是英语则无参就可以 if (originValue instanceof Enum) { return value.equals(originValue.toString().toLowerCase(Locale.ROOT)); } if (originValue instanceof ArrayList || originValue instanceof Set) { if (CollectionUtils.isEmpty((Collection<?>) originValue) && CollectionUtils.isEmpty((Collection<?>) value)) { return true; } if (CollectionUtils.isEmpty((Collection<?>) originValue) && !CollectionUtils.isEmpty((Collection<?>) value)) { return false; } JSONArray array= JSONArray.parseArray(JSON.toJSONString(value)); JSONArray originArray= JSONArray.parseArray(JSON.toJSONString(originValue)); for (int i = 0; i< array.size(); i++) { Map<String, Object> map = JSONObject.parseObject(array.get(i).toString(), HashMap.class); Map<String, Object> originMap = JSONObject.parseObject(originArray.get(i).toString(), HashMap.class); for (Map.Entry item : map.entrySet()) { // 无线调用,这思路和布局绝了~ boolean result = compareValue(item.getValue(), originMap.get(item.getKey())); if (!result) { log.info("对比失败,key===>{}", item.getKey()); return false; } } } return true; } if (originValue instanceof JSONArray) { if (originValue == null && value == null) { return true; } if (value != null && originValue == null) { return false; } JSONArray valueArray = (JSONArray) value; JSONArray originArray = (JSONArray) originValue; if (valueArray.size() == 0 && originArray.size() == 0) { return true; } if (valueArray.size() > 0 && originArray.size() == 0) { return false; } for (int i = 0; i < valueArray.size(); i++) { JSONObject valueObject = (JSONObject) valueArray.get(0); JSONObject originObject = (JSONObject) originArray.get(0); for (Map.Entry item : valueObject.entrySet()) { boolean result = compareValue(item.getValue(), originObject.get(item.getKey())); if (!result) { log.info("对比失败,key===>{}", item.getKey()); return false; } } } return true; } // 如果以上都没有匹配到,则按照Object处理 String valueStr = JSONObject.toJSONString(value); String originStr = JSONObject.toJSONString(originValue); if (StringUtils.isEmpty(valueStr) && StringUtils.isEmpty(originStr)) { return true; } if (!StringUtils.isEmpty(valueStr) && StringUtils.isEmpty(originStr)) { return false; } //上面如果没有匹配到,按对象处理,将对象转map Map<String, Object> valueMap = JSON.parseObject(valueStr, new TypeReference<Map<String, Object>>() {}); Map<String, Object> originMap = JSON.parseObject(JSON.toJSONString(originValue), new TypeReference<Map<String, Object>>() {}); for (Map.Entry item : valueMap.entrySet()) { boolean result = compareValue(item.getValue(), originMap.get(item.getKey())); if (!result) { log.info("对比失败,key===>{}", item.getKey()); return false; } } return true; } }
注: 以上只是大佬开发的 对比工具 0.1版本,之后会继续开发优化,扩展更多的数据对比场景。还会提供一个注解版。让我们拭目以待!