基于Springboot与注解比较两个对象对应字段的属性值
前言:比较两个对象同一字段的不同值,并对字段进行释义,对字典值进行转义,输出中文修改说明,可用于操作日志的输出。
一、字典值缓存
1、应用上下文工具类
启动类中设置应用上下文,从而可以在工具类中注入服务层
1 @SpringBootApplication 2 public class DemoApplication { 3 4 public static void main(String[] args) { 5 ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args); 6 SpringContextUtil.setApplicationContext(applicationContext); 7 } 8 9 }
应用上下文工具类
1 public class SpringContextUtil { 2 3 private static ApplicationContext applicationContext; 4 5 public static ApplicationContext getApplicationContext() { 6 return applicationContext; 7 } 8 9 public static void setApplicationContext(ApplicationContext applicationContext) { 10 SpringContextUtil.applicationContext = applicationContext; 11 } 12 13 //返回指定bean的实例 14 public static Object getBean(String beanId) { 15 return applicationContext.getBean(beanId); 16 } 17 }
2、字典值缓存
字典表结构,忽略字典表服务层代码,缓存于HashMap中。
1 public class DictMap { 2 3 @Autowired 4 private static SysDictService dictService; 5 6 private static HashMap<String, String> hashMap = new HashMap<>(); 7 //静态方法在程序启动的时候只加载一次,这样为了让查询方法只去数据库查询一次 8 static { 9 //获取应用上下文对象 10 ApplicationContext ctx = SpringContextUtil.getApplicationContext(); 11 //获取字典服务实例 12 dictService = ctx.getBean(SysDictService.class); 13 queryDict(); 14 } 15 //从数据库中取值放入到HashMap中 16 public static void queryDict(){ 17 hashMap.clear(); 18 List<SysDict> dicts = dictService.findAll(); 19 for (SysDict dict : dicts) { 20 hashMap.put(dict.getCode(),dict.getDictName()); 21 } 22 } 23 24 /** 25 * 获取单个字典值的说明(中文) 26 * @param dictType 27 * @param dictValue 28 * @return 29 */ 30 public static String getDictName(String dictType,String dictValue){ 31 StringBuilder sb = new StringBuilder(); 32 StringBuilder keySb = sb.append(dictType).append("_").append(dictValue); 33 String key = keySb.toString(); 34 String value = hashMap.get(key); 35 return value; 36 } 37 38 /** 39 * 获取多个字典值的说明(中文) 40 * @param dictType 41 * @param dictValue 42 * @return 43 */ 44 public static String getDictNames(String dictType,String dictValue){ 45 String dictStr = ""; 46 if (StringUtils.isNotBlank(dictValue)){ 47 //以"|"为分隔符 48 String[] split = dictValue.split("\\|"); 49 for (int i = 0; i < split.length; i++) { 50 if (i == split.length - 1) { 51 dictStr += DictMap.getDictName(dictType, split[i]); 52 } else { 53 dictStr += DictMap.getDictName(dictType, split[i]) + "|"; 54 } 55 } 56 } 57 return dictStr; 58 } 59 60 }
二、注解
字段注解,字段中文名,作用于实体类需要比较的字段。
1 /** 2 * @Description: 需要显示的实体字段中文名 3 */ 4 @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 5 @Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法 6 @Documented//说明该注解将被包含在javadoc中 7 public @interface FieldMeta { 8 String name() default ""; 9 String description() default ""; 10 }
三、实体类的写法
使用注解标志对应字段中文名,重写字典字段的get方法,进行字典值转义。
1 @Data 2 public class ContrastClass { 3 4 @FieldMeta(name = "年龄") 5 private int age; 6 @FieldMeta(name = "姓名") 7 private String name; 8 @FieldMeta(name = "生日") 9 private Date date; 10 11 private String status; 12 13 @FieldMeta(name = "状态") 14 private String statusStr; 15 16 public String getStatusStr() { 17 return DictMap.getDictNames("STAFF_STATUS",status); 18 } 19 }
四、对比工具类
通过反射对两个对象的相关字段进行比较
1 /** 2 * @Description: 比较两个对象的变化 3 */ 4 public class ContrastUtils { 5 6 /**记录每个修改字段的分隔符*/ 7 public static final String separator = ";"; 8 9 /** 10 * 比较两个对象,并返回不一致的信息 11 * @param oldObj 旧对象 12 * @param newObj 新对象 13 * @return 14 * @throws ClassNotFoundException 15 * @throws IllegalAccessException 16 */ 17 public static String compareTwoObj(Object oldObj,Object newObj ) throws IllegalAccessException, IntrospectionException, InvocationTargetException { 18 19 String str = ""; 20 //获取对象的class 21 Class<?> oldClass = oldObj.getClass(); 22 Class<?> newClass = newObj.getClass(); 23 //获取对象的属性列表 24 Field[] oldFields = oldClass.getDeclaredFields(); 25 Field[] newFields = newClass.getDeclaredFields(); 26 for (int i = 0; i < oldFields.length; i++) { 27 if ("serialVersionUID".equals(oldFields[i].getName())) { 28 continue; 29 } 30 oldFields[i].setAccessible(true); 31 newFields[i].setAccessible(true); 32 33 // 这样就获取到这个注解属性了 34 FieldMeta fieldChinese = oldFields[i].getAnnotation(FieldMeta.class); 35 //无对应注解则说明该字段无需比较 36 if (fieldChinese == null || StringUtils.isBlank(fieldChinese.name())) { 37 continue; 38 } 39 //获取注解中字段名 40 String fieldName = fieldChinese.name(); 41 42 PropertyDescriptor oldPd = new PropertyDescriptor(oldFields[i].getName(), oldClass); 43 PropertyDescriptor newPd = new PropertyDescriptor(newFields[i].getName(), newClass); 44 Method oldReadMethod = oldPd.getReadMethod(); 45 Method newReadMethod = newPd.getReadMethod(); 46 //获取对应字段的值 47 Object oldValue = oldReadMethod.invoke(oldObj); 48 Object newValue = newReadMethod.invoke(newObj); 49 50 /**获取差异字段*/ 51 str = getDifferenceFieldStr(str, i, fieldName, oldValue, newValue); 52 53 } 54 return str; 55 } 56 57 /** 58 * 获取差异字段新旧值 59 * @param str 60 * @param i 61 * @param fieldName 62 * @param oldValue 63 * @param newValue 64 * @return 65 */ 66 private static String getDifferenceFieldStr(String str, int i, String fieldName, Object oldValue, Object newValue) { 67 68 if (null == oldValue || StringUtils.isBlank(oldValue.toString())){ 69 oldValue = "无"; 70 } 71 if (null == newValue || StringUtils.isBlank(newValue.toString())){ 72 newValue = "无"; 73 } 74 if (!oldValue.equals(newValue)) { 75 if (i != 0) { 76 str += separator; 77 } 78 str += "字段名称:" + fieldName + ",旧值:" + oldValue + ",新值:" + newValue; 79 } 80 return str; 81 } 82 83 }
五、测试类
1 @RestController 2 @RequestMapping("demo") 3 public class DictController { 4 5 @GetMapping("/test") 6 public ResultVo test() throws IllegalAccessException, InterruptedException, IntrospectionException, InvocationTargetException { 7 8 ContrastClass test1 = new ContrastClass(); 9 test1.setAge(10); 10 test1.setDate(new Date()); 11 test1.setStatus("1|2"); 12 Thread.sleep(100); 13 14 ContrastClass test2 = new ContrastClass(); 15 test2.setAge(20); 16 test2.setName("李四"); 17 test2.setStatus("2|4"); 18 test2.setDate(new Date()); 19 String s = ContrastUtils.compareTwoObj(test1, test2); 20 21 return new ResultVo().success(s); 22 } 23 24 }
六、结果