弊端:
如果需要增减属性,需要增减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