需求分析
在开发过程中,为了记录系统操作行为,我们通常需要实现操作日志功能。本案例要求对系统中增、删、改等接口的操作日志记录到数据库表中,具体需求包括:
-
日志内容:
-
操作人(从请求头中的 Token 中解析获取当前登录用户的 ID)。
-
操作时间。
-
执行方法的全类名、方法名。
-
方法运行时参数、返回值、执行耗时。
-
-
日志存储:日志信息需要保存到数据库表
operate_log
中。 -
AOP 设计:
-
采用
@Around
环绕通知实现。 -
切入点表达式基于自定义注解
@Log
,方便灵活标注需要记录日志的方法。
-
代码解读
日志实体类:OperateLog
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {private Integer id; //IDprivate Integer operateEmpId; //操作人IDprivate LocalDateTime operateTime; //操作时间private String className; //操作类名private String methodName; //操作方法名private String methodParams; //操作方法参数private String returnValue; //操作方法返回值private Long costTime; //操作耗时//页面展示字段private String operateEmpName; //操作人姓名
}
-
注解
@Data
:-
自动生成 Getter、Setter 等常用方法。
-
-
字段定义:
-
包含日志记录的所有字段,例如操作人、操作时间、方法名、返回值等。
-
-
页面展示字段:
-
添加
operateEmpName
字段,方便在日志展示页面中显示操作人姓名。
-
-
构造方法:
-
使用
@AllArgsConstructor
和@NoArgsConstructor
提供全参和无参构造器。
-
AOP 切面类:OperateLogAspect
@Slf4j
@Aspect
@Component
public class OperateLogAspect {@Autowiredprivate OperateLogMapper operateLogMapper;@Autowiredprivate HttpServletRequest request;@Around("@annotation(com.zhang.anno.Log)")public Object record(ProceedingJoinPoint pjp) throws Throwable {// 获取操作人的 IDString jwt = request.getHeader("token");Claims claims = JwtUtils.parseJWT(jwt);Integer empId = Integer.parseInt(claims.get("id").toString());// 记录操作时间LocalDateTime operateTime = LocalDateTime.now();// 获取类名和方法名String className = pjp.getTarget().getClass().getName();String methodName = pjp.getSignature().getName();// 获取方法参数String methodParams = Arrays.toString(pjp.getArgs());// 记录方法执行时间long begin = System.currentTimeMillis();Object result = pjp.proceed(); // 调用原始方法long end = System.currentTimeMillis();// 构建操作日志对象OperateLog operateLog = new OperateLog(null, empId, operateTime, className, methodName, methodParams, result.toString(), end - begin, null);// 保存日志到数据库operateLogMapper.insert(operateLog);return result;}
}
-
注解
@Aspect
和@Component
:-
@Aspect
表示这是一个切面类。 -
@Component
将切面类注册为 Spring 的 Bean。@Aspect
本身只标识一个类为切面,并不意味着该类会自动生效。配合@Component
配置注册为 Bean 后,Spring AOP 框架才能找到并应用这个切面逻辑。
-
-
切入点表达式:
@Around("@annotation(com.zhang.anno.Log)")
-
只对标注了
@Log
注解的方法生效。
-
-
ProceedingJoinPoint
:-
用于获取切点方法的详细信息,例如类名、方法名、参数等。
-
使用
proceed()
方法调用原始业务逻辑。
-
-
日志数据的采集:
-
操作人通过解析 Token 获取。
-
操作时间、方法参数、返回值、执行耗时均在切面逻辑中记录。
-
-
日志的存储:
-
通过调用
OperateLogMapper
的insert
方法将日志保存到数据库中。
-
数据库访问层:OperateLogMapper
@Mapper
public interface OperateLogMapper {@Insert("insert into operate_log (operate_emp_id, operate_time, class_name, method_name, method_params, return_value, cost_time) " +"values (#{operateEmpId}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")public void insert(OperateLog log);
}
代码说明
-
注解
@Mapper
:-
标记为 MyBatis 的 Mapper 接口。
-
-
SQL 语句:
-
INSERT
语句将日志各字段存储到operate_log
表。
-
-
参数绑定:
-
使用
#{}
语法绑定OperateLog
对象中的字段。
-
总结
通过使用 Spring AOP ,本案例实现了增、删、改接口操作日志的记录功能,主要包含以下亮点:
-
高内聚低耦合:通过 AOP 实现日志功能,与业务代码解耦,提高代码复用性和可维护性。
-
灵活扩展:使用自定义注解
@Log
标注需要记录日志的方法,方便灵活扩展。 -
操作全面:日志记录内容详尽,包括操作人、操作时间、类名、方法名、参数、返回值及耗时。