使用反射完成 Spring Boot 程序对象操作记录
1. 引言
1.1 背景和目的
在开发业务系统时,常需要记录方法调用和对象操作的日志,例如记录谁在什么时候对哪些对象进行了什么操作。这种功能在审计、调试和系统回溯中非常重要。然而,为了避免重复性代码,可以利用反射实现对象操作记录,既通用又灵活。
1.2 为什么选择反射
- 动态性:反射可以在运行时操作类和对象的信息,适应复杂多变的业务需求。
- 灵活性:通过反射可以动态获取或修改对象的字段值、调用方法,避免硬编码特定字段或方法名。
- 与 AOP 配合:反射结合 Spring AOP,可以在切面中对任意方法或对象的操作进行记录,而不干扰核心业务逻辑。
2. 基本概念
2.1 反射的基础知识
-
反射是什么
Java 反射是指在运行时动态加载类、获取类的信息(如字段、方法、构造函数)、并进行操作的机制。 -
反射的核心类
Class
:获取类的元信息。Field
:代表类的字段,可以动态获取或设置字段值。Method
:代表类的方法,可以动态调用方法。Constructor
:表示构造函数,可以动态创建实例。
-
反射的常用操作
- 动态加载类:
Class.forName("com.example.MyClass")
- 获取字段值:
Field field = clazz.getDeclaredField("fieldName")
- 调用方法:
Method method = clazz.getMethod("methodName", paramTypes)
- 动态加载类:
2.2 Spring Boot 对象操作的场景
- 操作日志记录:在 Controller 或 Service 层记录方法调用及对象的状态变化。
- 动态数据处理:基于反射读取或更新实体类的字段值,例如批量处理数据库对象。
- 调试与性能分析:动态注入调试逻辑或分析方法的调用频率和耗时。
3. 实现对象操作记录的基本思路
- 获取修改前后两个对象,使用反射获取所有字段进行比对,从而记录修改的字段。
4. 代码实现
public void trackChanges(Object oldObj, Object newObj) {
Field[] fields = oldObj.getClass().getDeclaredFields();
StringBuilder content = new StringBuilder();
for (Field field : fields) {
field.setAccessible(true);
if (Modifier.isPrivate(field.getModifiers())) {
try {
// 1.获取字段中文名
String fullName = field.getName();
String fieldName = fullName.substring(fullName.lastIndexOf(".") + 1);
ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
if (apiModelProperty != null) {
fieldName = apiModelProperty.value();
}
// 2.记录修改操作
Object oldValue = field.get(oldObj);
Object newValue = Optional.ofNullable(field.get(newObj)).orElse("");
if (Objects.nonNull(oldValue) && !oldValue.equals(newValue)) {
content.append(fieldName).append("从: ").append(oldValue).append(",改为: ").append(newValue).append(";");
}
} catch (IllegalAccessException e) {
throw new RuntimeException("日志反射填充出错!");
}
}
}
}
5.扩展说明
- 本次仅简单完成操作记录内容,后续有时间会考虑使用Spring AOP进行扩展以及源码提供。