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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 在业务场景中可能有这样的需求:同一个类的两个对象(一个数数据库中获取的上一次的属性,一个是前端传来的修改过的属性),需要判断哪个属性被修改了。那么有一些童鞋可能采用大量的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 liuwangyangedu@163.com

* @date 2019年03月16日

*/

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Field2Value {

   String alias() default "";

}


package com.chujianyun.field2value.annotation;

import java.lang.annotation.*;

/**

* 需要忽略的属性

*

* @author liuwangyangedu@163.com

* @date: 2019-04-08 10:15

*/

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Ignore {

}

工具类


package com.chujianyun.field2value.utils;

import com.chujianyun.field2value.annotation.Field2Value;

import com.chujianyun.field2value.annotation.Ignore;

import java.lang.reflect.Field;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;

/**

* 对象属性名到其值的映射工具

* <p>

* 可用来比较值发生了变化的属性

*

* @author liuwangyangedu@163.com

* @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 属性--> 值

    */

   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 (field.isAnnotationPresent(Ignore.class)) {

               continue;

           }

           // 解析所有

           if (resolveAllField) {

               field2hashMap.put(key, field.get(object));

               continue;

           }

           // 只解析带@Field2Value注解的

           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.field2value;

import com.chujianyun.field2value.annotation.Field2Value;

import com.chujianyun.field2value.annotation.Ignore;

import lombok.Data;

/**

* Cat测试实体

*

* @author liuwangyangedu@163.com

* @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;

   @Ignore

   private String role;

   @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 liuwangyangedu@163.com

* @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");

   }

}


 

————————————————

版权声明:本文为CSDN博主「明明如月学长」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/w605283073/article/details/88606762

相关文章
|
6月前
|
JavaScript 前端开发
如何删除对象中的属性?如何想对象中添加属性?
如何删除对象中的属性?如何想对象中添加属性?
63 0
|
2月前
|
JavaScript 前端开发
判断对象是否含有改属性,三个方法
JavaScript中判断对象是否包含属性的三种方法:1. 使用`'property' in object`检查自有属性和继承属性;2. 使用`object.hasOwnProperty('property')`仅检查自有属性;3. 使用`if (object.property)`判断,但返回属性值。
36 2
判断对象是否含有改属性,三个方法
|
6月前
|
测试技术
反射获取或修改对象属性的值
* 获取单个对象的所有键值对
52 3
|
6月前
|
XML JSON 移动开发
BpmnJS 元素属性的updateProperties 和updateModdleProperties的属性更新区别
BpmnJS 元素属性的updateProperties 和updateModdleProperties的属性更新区别
238 1
lodash遍历分配对象的属性,忽略相同属性的值
lodash遍历分配对象的属性,忽略相同属性的值
95 0
|
JavaScript
js获取对象属性的两种方法,object.属性名,[‘属性名’ ]
js获取对象属性的两种方法,object.属性名,[‘属性名’ ]
1004 0
js获取对象属性的两种方法,object.属性名,[‘属性名’ ]
【java】java反射机制,动态获取对象的属性和对应的参数值,并属性按照字典序排序,Field.setAccessible()方法的说明【可用于微信支付 签名生成】
方法1:通过get()方法获取属性值   package com.sxd.test.controller; public class FirstCa{ private Integer num; private String name; private Boole...
2760 0
|
编译器 C++
C++中不要随便返回对象的引用
C++中不要随便返回对象的引用
213 0
C++中不要随便返回对象的引用
|
存储 Kotlin
【Kotlin】属性 与 幕后字段 ( 属性声明 | 属性初始化器 | 属性访问器 | field 属性幕后字段 | lateinit 延迟初始化属性 )
【Kotlin】属性 与 幕后字段 ( 属性声明 | 属性初始化器 | 属性访问器 | field 属性幕后字段 | lateinit 延迟初始化属性 )
221 0
【Kotlin】属性 与 幕后字段 ( 属性声明 | 属性初始化器 | 属性访问器 | field 属性幕后字段 | lateinit 延迟初始化属性 )
|
存储 JavaScript 前端开发
访问对象的属性,你知道有哪些方法?
相信对象对于每个使用JavaScript的人来说都不陌生。访问对象的属性几乎日常开发每天都在用。下面我们一起探索下有哪些方式可以实现属性的访问。
195 0