获取对象属性值改动的属性集合的正确姿势(拒绝大量If-else代码)

在业务场景中可能有这样的需求:

同一个类的两个对象(一个数数据库中获取的上一次的属性,一个是前端传来的修改过的属性),需要判断哪个属性被修改了。

那么有一些童鞋可能采用大量的if-else代码块对需要关注的属性进行判断。

弊端:

如果需要增减属性,需要增减If-else代码,会有代码量大,不容易维护等问题。

解决方案:

那么我们可以将属性和值的映射成键值对,比较属性的值是否相同来判断值是否改动过。

由于未必是所有属性比对,因此可以创建一个注解,允许只比对带有此注解的属性。

如果两个对象类型不同,只比较其中两个属性,且属性名不同怎么办?

那么可以在注解上加上别名,这样比对别名就好了。

上代码(建议从github拉取):

github地址:https://github.com/chujianyun/filed2value

pom文件配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.chujianyun</groupId>
    <artifactId>field2hash</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>



    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

注解类:

package com.chujianyun.field2hash.annotation;

import java.lang.annotation.*;

/**
 * 待校验的属性
 * 允许指定别名
 *
 * @author [email protected]
 * @date 2019年03月16日
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Field2Value {
    String alias() default "";
}

工具类

package com.chujianyun.field2hash.utils;

import com.chujianyun.field2hash.annotation.Field2Value;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 对象属性名到其值的映射工具
 * 可用来
 *
 * @author [email protected]
 * @date 2019年03月16日
 */
public class Field2ValueUtil {


    /**
     * 根据对象和属性名+别名的集合获取属性集合
     *
     * @param object            待解析的对象
     * @param fieldOrAliasNames 属性名或者别名的集合
     * @return 属性集合
     */
    public static Set<Field> getFieldsByFieldOrAliasNames(Object object, Set<String> fieldOrAliasNames) {
        if (object == null || fieldOrAliasNames == null || fieldOrAliasNames.isEmpty()) {
            return new HashSet<>(0);
        }

        Set<Field> fields2get = new HashSet<>(fieldOrAliasNames.size());
        Class<?> clazz = object.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            // 带注解
            if (field.isAnnotationPresent(Field2Value.class)) {
                Field2Value annotation = field.getAnnotation(Field2Value.class);
                String alias = annotation.alias();
                if (fieldOrAliasNames.contains(alias) || fieldOrAliasNames.contains(field.getName())) {
                    fields2get.add(field);
                    break;
                }
            } else {
                if (fieldOrAliasNames.contains(field.getName())) {
                    fields2get.add(field);
                }
            }
        }
        return fields2get;
    }

    /**
     * 根据属性的名称或者别名的名称获取属性的值
     *
     * @param object           对象
     * @param fieldNameOrAlias 属性名或别名
     * @return 该属性的值
     */
    public static Object getValueByFieldNameOrAlias(Object object, String fieldNameOrAlias) throws IllegalAccessException {
        Class<?> clazz = object.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();

        Field field2resolve = null;
        for (Field field : declaredFields) {
            // 直接属性名相同
            if (field.getName().equals(fieldNameOrAlias)) {
                field2resolve = field;
                break;
            }
            // 别名相同
            if (field.isAnnotationPresent(Field2Value.class)) {
                Field2Value annotation = field.getAnnotation(Field2Value.class);
                String alias = annotation.alias();
                if (!"".equals(alias) && alias.equals(fieldNameOrAlias)) {
                    field2resolve = field;
                    break;
                }
            }

        }
        if (field2resolve != null) {
            field2resolve.setAccessible(true);
            return field2resolve.get(object);
        }
        return null;
    }


    /**
     * 获取两个对象属性的值不同的所有属性名称
     *
     * @param object1                 第一个对象
     * @param object2                 第二个对象
     * @param onlyCompareCommonFields 设计费
     * @return 属性的值不同的所有属性名称
     */
    public static Set<String> getDifferentValueFieldOrAliasNames(Object object1, Object object2, boolean resolveAllField, boolean onlyCompareCommonFields) throws IllegalAccessException {

        Map<String, Object> field2ValuePair1 = getField2ValuePair(object1, resolveAllField);
        Set<String> keySet1 = field2ValuePair1.keySet();
        Map<String, Object> field2ValuePair2 = getField2ValuePair(object2, resolveAllField);
        Set<String> keySet2 = field2ValuePair2.keySet();

        if (keySet1.isEmpty()) {
            return keySet2;
        }

        if (keySet2.isEmpty()) {
            return keySet1;
        }

        Set<String> fieldsWithDifferentValue = new HashSet<>();

        // 只比较公共属性
        for (Map.Entry<String, Object> entry : field2ValuePair1.entrySet()) {
            String fieldName = entry.getKey();
            Object value1 = entry.getValue();

            Object value2 = field2ValuePair2.get(fieldName);

            boolean sameHashCode = (value1.hashCode() == value2.hashCode());
            boolean sameObject = value1.equals(value2);
            if (!(sameHashCode && sameObject)) {
                fieldsWithDifferentValue.add(fieldName);
            }
        }

        // 不相同的fields
        if (!onlyCompareCommonFields) {
            Set<String> keySet1Copy = new HashSet<>(keySet1);
            Set<String> keySet2Copy = new HashSet<>(keySet2);
            keySet1.removeAll(keySet2);
            keySet2Copy.removeAll(keySet1Copy);

            fieldsWithDifferentValue.addAll(keySet1);
            fieldsWithDifferentValue.addAll(keySet2Copy);
        }
        return fieldsWithDifferentValue;
    }

    /**
     * 获取属性及其对应值得hash值(可能有hash冲突,谨慎使用)
     *
     * @param resolveAllField 解析所有属性
     * @return 属性--> 值hash
     */
    public static <T> Map<String, Integer> getField2HashPair(T object, boolean resolveAllField) throws IllegalAccessException {

        if (object == null) {
            return new HashMap<>(0);
        }

        Map<String, Object> field2ValuePair = getField2ValuePair(object, resolveAllField);
        Map<String, Integer> field2hashPairMap = new HashMap<>(field2ValuePair.size());

        field2ValuePair.forEach((key, value) -> field2hashPairMap.put(key, value.hashCode()));
        return field2hashPairMap;
    }

    /**
     * 获取属性及其对应值的映射(推荐使用)
     *
     * @param resolveAllField 解析所有属性
     * @return 属性--> 值hash
     */
    public static <T> Map<String, Object> getField2ValuePair(T object, boolean resolveAllField) throws IllegalAccessException {

        if (object == null) {
            return new HashMap<>(0);
        }

        Class<?> clazz = object.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        Map<String, Object> field2hashMap = new HashMap<>(declaredFields.length);
        for (Field field : declaredFields) {
            field.setAccessible(true);
            String key = field.getName();

            if (resolveAllField) {
                field2hashMap.put(key, field.get(object));
                continue;
            }

            if (field.isAnnotationPresent(Field2Value.class)) {
                Field2Value annotation = field.getAnnotation(Field2Value.class);
                String alias = annotation.alias();
                if (!"".equals(alias)) {
                    key = alias;
                }
                field2hashMap.put(key, field.get(object));
            }
        }
        return field2hashMap;
    }

}

实体类

package com.chujianyun.field2hash;

import com.chujianyun.field2hash.annotation.Field2Value;
import lombok.Data;

/**
 * Cat测试实体
 *
 * @author [email protected]
 * @date 2019年03月16日
 */
@Data
public class Cat implements Cloneable {
    private String name;

    private Byte age;

    @Field2Value(alias = "nick")
    private String nickName;

    @Field2Value
    private String ownerName;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}

测试类:

package com.chujianyun.field2hash.utils;

import com.chujianyun.field2hash.Cat;
import org.apache.commons.lang3.ObjectUtils;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;

/**
 * Field2ValueUtil测试类
 *
 * @author [email protected]
 * @date 2019年03月16日
 */
public class Field2ValueUtilTest {

    private Cat cat = null;

    private String age = "age";

    @Before
    public void init() {
        // 原始属性
        cat = new Cat();
        cat.setAge(Byte.parseByte("1"));
        cat.setName("喵咪");
        cat.setNickName("tomcat");
        cat.setOwnerName("了凡");
    }

    @Test
    public void filed2hashTest() throws IllegalAccessException {

        Map<String, Integer> field2HashPair = Field2ValueUtil.getField2HashPair(cat, false);
        System.out.println("修改前" + field2HashPair);

        cat.setOwnerName("张无忌");

        Map<String, Integer> field2HashPair2 = Field2ValueUtil.getField2HashPair(cat, false);
        System.out.println("修改后" + field2HashPair2);
    }

    /**
     * 获取属性值不同的属性名
     */
    @Test
    public void getDifferentValueFieldNames() throws IllegalAccessException {

        Cat catClone = ObjectUtils.clone(cat);

        catClone.setOwnerName("张无忌");
        // 两个对象不同的属性名活别名集合
        Set<String> differentValueFieldOrAliaNames = Field2ValueUtil.getDifferentValueFieldOrAliasNames(cat, catClone, false, true);
        System.out.println(differentValueFieldOrAliaNames);
        assertEquals(differentValueFieldOrAliaNames.size(), 1);

        // 属性名或别名集合
        for (String fieldNameOrAlias : differentValueFieldOrAliaNames) {
            System.out.println(Field2ValueUtil.getValueByFieldNameOrAlias(catClone, fieldNameOrAlias));
        }

        // 属性集合
        Set<Field> fieldsByFieldOrAliasNames = Field2ValueUtil.getFieldsByFieldOrAliasNames(catClone, differentValueFieldOrAliaNames);
         System.out.println(fieldsByFieldOrAliasNames);
    }

    /**
     * 解析待注解的属性
     */
    @Test
    public void getField2HashPair() throws IllegalAccessException {
        Map<String, Integer> field2HashPair1 = Field2ValueUtil.getField2HashPair(cat, false);
        System.out.println(field2HashPair1);
        assertNull(field2HashPair1.get(age));
    }

    /**
     * 解析所有属性
     */
    @Test
    public void getField2HashPairAllFields() throws IllegalAccessException {
        Map<String, Integer> field2HashPair = Field2ValueUtil.getField2HashPair(cat, true);
        System.out.println(field2HashPair);
        assertNotEquals(field2HashPair.get(age), "1");
    }
}

猜你喜欢

转载自blog.csdn.net/w605283073/article/details/88606762
今日推荐