利用反射比较并记录对象修改的值

8/17/2022 多线程

# 前述

这几天项目有一个需求,就是在更新采购申请表信息时,需要记录更新了哪些字段的信息。如果字段少的话倒还好,两个新旧对象循环去比较就行了,但是字段很多的情况就不适用了,而且后续也可能还会有这样的需求,所以应该整一个通用的比较工具类去实现。

我们知道,可以通过getDeclaredField来获取一个实例的字段,通过Fieldget()方法可以获取到对应字段在实例中的数据,这样我们就可以通过反射封装一个工具类,来比较新旧两个对象相同字段不同的值的记录了。

# 代码示例

# 新增注解

/**
 * 用于比较并记录相同对象相同属性的不同值
 *
 * @author LiJunYi
 * @date 2022/08/17
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ForUpdate
{
    /**
     * 字段名
     *
     * @return {@link String}
     */
    String fieldName() default "";

    /**
     * int转换str
     * 0 否 1是
     * @return boolean
     */
    boolean intConvertStr() default false;

    /**
     * 是否从缓存中读取
     *
     * @return boolean
     */
    boolean readFromCache() default false;

    /**
     * 从哪个缓存读取
     *
     * @return {@link ReadFromCacheType}
     */
    ReadFromCacheType readFromCacheType() default ReadFromCacheType.NONE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# ReadFromCacheType枚举

/**
 * @version 1.0.0
 * @className: ReadFromCacheType
 * @description: 从指定缓存中获取数据
 * @see InitialLoadSomeMap#initialSomeMap()
 * @author: LiJunYi
 * @create: 2022/8/17 13:48
 */
public enum ReadFromCacheType
{

    /**
     * 缓存数据
     */
    CONTRACT_TYPE_REASON_MAP,

    /**
     * 缓存数据
     */
    PRICING_MODE_MAP,

    /**
     * 缓存数据
     */
    SECRET_POINT_MAP,

    /**
     * 没有
     */
    NONE
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 实体类添加注解

@Data
public class CgModel implements Serializable {

    private static final long serialVersionUID=1L;

    @ForUpdate(fieldName  = "合同类型")
    private String contractType;

    @ForUpdate(fieldName  = "合同类型理由", readFromCache = true , readFromCacheType = ReadFromCacheType.CONTRACT_TYPE_REASON_MAP)
    private String contractTypeReasonId;

    // .......
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 工具类

/**
 * @version 1.0.0
 * @className: BeanUpdateUtils
 * @description: 通过反射比较并记录对象相同属性字段新旧值对比
 * @author: LiJunYi
 * @create: 2022/8/17 9:28
 */
@Slf4j
public class BeanUpdateUtils
{
    /**
     * 返回新旧值结果
     *
     * @param newBean 新对象
     * @param oldBean 旧对象
     * @return {@link String}
     */
    public static <T> String getChangedFields(T newBean, T oldBean)
    {
        // 获取属性
        Field[] fields = newBean.getClass().getDeclaredFields();
        StringBuilder builder = new StringBuilder();
        for(Field field : fields)
        {
            field.setAccessible(true);
            if (field.isAnnotationPresent(ForUpdate.class)) 
            {
                try 
                {
                    Object newValue = Optional.ofNullable(field.get(newBean)).orElse("");
                    Object oldValue = Optional.ofNullable(field.get(oldBean)).orElse("");
                    if(!Objects.equals(newValue, oldValue))
                    {
                        builder.append("将 【");
                        builder.append(field.getAnnotation(ForUpdate.class).fieldName());
                        builder.append("】 由 【");
                        builder.append(oldValue);
                        builder.append("】 修改为 【");
                        builder.append(newValue);
                        builder.append("】;\n");
                    }
                } catch (Exception e) 
                {
                    log.error("记录采购申请单修改记录异常,异常原因:{}",e.getMessage());
                }
            }
        }
        return builder.toString();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50