前言
反射是JAVA语言主要的特征之一。它是允许运行中的java程序对自身进行检查。
在java程序中被private修饰的只能在内部访问,外部是不行的,
若想在外部访问private修饰的是可以通过反射实现的。
反射是可以直接操作私有属性的,反射是一个可以在运行时获取一个类的所有信息,操作这些信息的。
常用方法
//获取到对象的class
Class<?> oldClass = oldObj.getClass();
//对象的包名
oldClass.getPackage().getName()
//类名User
oldClass.getSimpleName()
//完整类名com.xxx.User
oldClass.getSimpleName()
//对象的属性列表(比如实体类的各个字段)
oldClass.getDeclaredFields()
其他知识setAccessible
//将此对象的 accessible 标志设置为指示的布尔值。
// 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
// 值为 false 则指示反射的对象应该实施 Java 语言访问检查;
// 实际上setAccessible是启用和禁用访问安全检查的开关,
// 并不是为true就能访问为false就不能访问 ;
setAccessible(true);
反射获取修改对象修改的内容
1:自定义注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法
@Documented//说明该注解将被包含在javadoc中
public @interface FieldMeta {
String name() default "";
String description() default "";
}
2:工具类
import org.apache.commons.lang3.StringUtils;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 反射
* 通过反射比较两个对象修改字段的区别
* <p>
* ***************************************
* 反射是Java程序开发语言的特征之一,它允许运行中的java程序对自身进行检查。
* 被private封装的只能内部访问,外部是不行的,但是通过反射是可以直接操作私有属性的。
* 反射是一个可以在运行时获取一个类的所有信息
*/
public class ContrastUtils {
/**
* 修改记录-字段的分割符号
*/
public static final String SEPARATOR = ";";
/**
* 比较两个对象,并且返回不一致的信息
*
* @return
*/
public static String compare(Object oldObj, Object newObj) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
String str = "";
//1:获取到对象的class
Class<?> oldClass = oldObj.getClass();
Class<?> newClass = newObj.getClass();
//2:获取到对象的属性列表
Field[] oldFields = oldClass.getDeclaredFields();
Field[] newFields = newClass.getDeclaredFields();
for (int i = 0; i < oldFields.length; i++) {
if ("serialVersionUID".equals(oldFields[i].getName())) {
continue;
}
//将此对象的 accessible 标志设置为指示的布尔值。
// 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
// 值为 false 则指示反射的对象应该实施 Java 语言访问检查;
// 实际上setAccessible是启用和禁用访问安全检查的开关,
// 并不是为true就能访问为false就不能访问 ;
oldFields[i].setAccessible(true);
newFields[i].setAccessible(true);
//获取到字段上的注解
FieldMeta annotation = oldFields[i].getAnnotation(FieldMeta.class);
//当没有注解的时候跳过本次循环
if (annotation == null || StringUtils.isBlank(annotation.name())) {
continue;
}
//获取注解中的字段名
String name = annotation.name();
String description = annotation.description();
PropertyDescriptor oldPd = new PropertyDescriptor(oldFields[i].getName(), oldClass);
PropertyDescriptor newPd = new PropertyDescriptor(newFields[i].getName(), newClass);
//调用成员方法
Method oldReadMethod = oldPd.getReadMethod();
Method newReadMethod = newPd.getReadMethod();
//3:获取到参数数值
Object oldValue = oldReadMethod.invoke(oldObj);
Object newValue = newReadMethod.invoke(newObj);
if (StringUtils.isNotBlank(description)) {
//a=xxx
List<String> list = Arrays.asList(description.split(","));
Map<Object, Object> map = new HashMap<>();
list.forEach(item -> {
List<String> asList = Arrays.asList(item.split("="));
if (asList.size() > 0) {
map.computeIfAbsent(asList.get(0), k -> asList.get(1));
}
});
oldValue = map.get(oldValue.toString());
newValue = map.get(newValue.toString());
}
/**获取差异字段*/
str = getDifferenceFieldStr(str, i, name, oldValue, newValue);
}
return str;
}
/**
* 获取差异字段新旧值
*
* @param str
* @param i
* @param fieldName
* @param oldValue
* @param newValue
* @return
*/
private static String getDifferenceFieldStr(String str, int i, String fieldName, Object oldValue, Object newValue) {
if (null == oldValue || StringUtils.isBlank(oldValue.toString())) {
oldValue = "无";
}
if (null == newValue || StringUtils.isBlank(newValue.toString())) {
newValue = "无";
}
if (!oldValue.equals(newValue)) {
str += fieldName + "从 " + oldValue + ",修改为 " + newValue + SEPARATOR;
}
return str;
}
}
3:测试
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TestBo implements Serializable {
@FieldMeta(name = "性别",description = "1=男,2=女,3=未知")
private Integer sex;
@FieldMeta(name = "数量")
private int sum;
}
@Test
public void d() {
TestBo oldTest = TestBo.builder().sex(1).sum(800).build();
TestBo newTest = TestBo.builder().sex(2).sum(500).build();
try {
String compare = ContrastUtils.compare(oldTest, newTest);
System.out.println(compare);
} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
4:运行结果