有时候业务需要,需记录一条记录的修改历史,但是不能为完成任务而硬编码,不靠谱
这种情况可以使用java反射来完成
对对象属性的描述可以通过自定义注解来完成,读取里面的属性进而记录修改历史。
在对象的属性上面加上注解,value设置为属性的中文描述
工具了代码如下
util类(BeanChangeUtil)
1 import java.beans.PropertyDescriptor; 2 import java.lang.reflect.Field; 3 import java.lang.reflect.Method; 4 import java.util.Arrays; 5 6 public class BeanChangeUtil<T> { 7 public String contrastObj(Object oldBean, Object newBean) { 8 // 创建字符串拼接对象 9 StringBuilder str = new StringBuilder(); 10 // 转换为传入的泛型T 11 T pojo1 = (T) oldBean; 12 T pojo2 = (T) newBean; 13 // 通过反射获取类的Class对象 14 Class clazz = pojo1.getClass(); 15 // 获取类型及字段属性 16 Field[] fields = clazz.getDeclaredFields(); 17 return jdk8Before(fields, pojo1, pojo2, str,clazz); 18 // return jdk8OrAfter(fields, pojo1, pojo2, str,clazz); 19 } 20 21 // jdk8 普通循环方式 22 public String jdk8Before(Field[] fields,T pojo1,T pojo2,StringBuilder str,Class clazz){ 23 int i = 1; 24 try { 25 for (Field field : fields) { 26 if(field.isAnnotationPresent(PropertyMsg.class)){ 27 PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); 28 // 获取对应属性值 29 Method getMethod = pd.getReadMethod(); 30 Object o1 = getMethod.invoke(pojo1); 31 Object o2 = getMethod.invoke(pojo2); 32 if (o1 == null || o2 == null) { 33 continue; 34 } 35 if (!o1.toString().equals(o2.toString())) { 36 str.append(i + "、" + field.getAnnotation(PropertyMsg.class).value() + ":" + "修改前=>" + o1 + ",修改后=>" + o2 + "\n"); 37 i++; 38 } 39 } 40 } 41 } catch (Exception e) { 42 e.printStackTrace(); 43 } 44 return str.toString(); 45 } 46 47 // lambda表达式,表达式内部的变量都是final修饰,需要传入需要传入final类型的数组 48 public String jdk8OrAfter(Field[] fields, T pojo1, T pojo2, StringBuilder str, Class clazz){ 49 final int[] i = {1}; 50 Arrays.asList(fields).forEach(f -> { 51 if(f.isAnnotationPresent(PropertyMsg.class)){ 52 try { 53 PropertyDescriptor pd = new PropertyDescriptor(f.getName(), clazz); 54 // 获取对应属性值 55 Method getMethod = pd.getReadMethod(); 56 Object o1 = getMethod.invoke(pojo1); 57 Object o2 = getMethod.invoke(pojo2); 58 if (o1 == null || o2 == null) { 59 return; 60 } 61 if (!o1.toString().equals(o2.toString())) { 62 str.append(i[0] + "、" + f.getAnnotation(PropertyMsg.class).value() + ":" + "修改前=>" + o1 + "\t修改后=>" + o2 + "\n"); 63 i[0]++; 64 } 65 }catch (Exception e){ 66 e.printStackTrace(); 67 } 68 } 69 }); 70 return str.toString(); 71 } 72 }
自定义注解(PropertyMsg)
@Target
表示该注解可以用于什么地方,可能的ElementType参数有:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或enum声明
@Retention
表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE:注解将被编译器丢弃
CLASS:注解在class文件中可用,但会被VM丢弃
RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
@Document
将注解包含在Javadoc中
@Inherited
允许子类继承父类中的注解
1 import java.lang.annotation.*; 2 3 @Target(ElementType.FIELD) 4 @Retention(RetentionPolicy.RUNTIME) 5 @Documented 6 @Inherited 7 public @interface PropertyMsg { 8 String value(); 9 }
使用方式test
public class TestChange { public static void main(String[] args) { TestChange u1 = new TestChange("我是谁", "ok", 30,"刘德华"); TestChange u2 = new TestChange("我在哪", "no", 20,"郭富城"); BeanChangeUtil<TestChange> t = new BeanChangeUtil<>(); String str = t.contrastObj(u1, u2); if (str.equals("")) { System.out.println("未有改变"); } else { System.out.println(str); } } public TestChange() { } public TestChange(String about, String lock, Integer age, String name) { this.about = about; this.lock = lock; this.age = age; this.name = name; } @PropertyMsg("关于") private String about; private String lock; @PropertyMsg("年龄") private Integer age; @PropertyMsg("姓名") private String name; public String getAbout() { return about; } public void setAbout(String about) { this.about = about; } public String getLock() { return lock; } public void setLock(String lock) { this.lock = lock; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
OK,到位
https://github.com/chywx/JavaSE_chy
原文地址:https://www.cnblogs.com/chywx/p/10630457.html
时间: 2024-10-07 10:33:21