有时候业务需要,需记录一条记录的修改历史,但是不能为完成任务而硬编码,不靠谱
这种情况可以使用java反射来完成
对对象属性的描述可以通过自定义注解来完成,读取里面的属性进而记录修改历史。
在对象的属性上面加上注解,value设置为属性的中文描述
工具了代码如下
util类(BeanChangeUtil)
代码语言:javascript复制 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
允许子类继承父类中的注解
代码语言:javascript复制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
代码语言:javascript复制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;
}
}