Java项目实现【记录系统操作日志】功能
✨ 哈喽,屏幕前的每一位开发者朋友,你们好呀!✨
当你点开这篇文章时,或许正对着 IDE 里闪烁的光标发呆,或许刚解决一个卡了三天的 bug,正端着咖啡松口气 —— 不管此刻的你在经历什么,都想先和你说声:“辛苦了,同行者!” 👋
作为一名摸爬滚打多年的开发工程师,我始终觉得,我们敲出的每一行代码,不仅是业务逻辑的堆叠,更是无数个 “踩坑与爬坑” 的缩影。从第一次上线时的手忙脚乱,到如今能冷静应对突发 bug;从对着文档啃源码的迷茫,到能给新人讲清设计思路 —— 这些藏在键盘敲击声里的成长,太值得被好好梳理和分享了。 📝
所以,这一系列文章里,不会有太多高深的架构理论,也不会罗列晦涩的技术文档。我想聊的,是那些 “教科书里没写” 的实战细节:
比如上线前必做的 3 个自查动作(亲测能减少 80% 的线上问题)🛡️;
比如和产品经理 “友好沟通” 需求的 5 个小技巧(避免反复改需求到崩溃)🤝;
比如如何用最少的时间,快速定位线上性能瓶颈(曾靠这招拯救过一次紧急故障)🚀;
再比如那些看似 “浪费时间” 的重构,其实藏着怎样的长期价值…… ♻️
当然,更想和大家聊聊 “技术之外” 的事:如何平衡加班与生活(毕竟身体是敲代码的本钱)💪,如何在团队中清晰表达自己的想法(别让好方案被沉默埋没)🗣️,甚至是 “35 岁焦虑” 来袭时,我是如何调整心态的…… 🌱
如果你也和我一样,相信 “经验不是用来炫耀的资本,而是能帮同行少走弯路的路灯”,那不妨坐下来喝杯茶,一起在评论区聊聊:你最近遇到的最大挑战是什么?有没有哪个瞬间,让你觉得 “啊,原来我真的成长了”? 💬
毕竟,开发这条路从来不是孤军奋战。我们分享的每一个踩坑故事,都可能成为别人的指路牌;你留下的每一条评论,或许也会给我新的启发。 🌟
那么,准备好了吗?让我们开始这场 “代码背后的成长对话” 吧!接下来的每一篇,都等你来拍砖、补充、共鸣 —— 因为最好的经验,永远在交流里生长。 🌱
目录
一.需求分析
二.实现逻辑(完全由后端实现)
1.自定义注解OperatorLog
2.在所有controller的方法上,添加该自定义注解
3.编写切面类OperationLogAspect
三.效果展示
1.使用一下管理系统的某个功能
2.查看数据库,肯定会多一条操作日志
四.特别声明
一.需求分析
我要实现的效果是,我访问管理系统的任何一个功能之后,都要往数据库插入一条操作日志。
举例:我在管理系统中,新增了一条合同记录,此时就需要往数据库插入一条操作日志,样式如下
二.实现逻辑(完全由后端实现)
1.自定义注解OperatorLog
@Target(ElementType.METHOD)//表明这个注解只能加在方法上
@Retention(RetentionPolicy.RUNTIME)//表明这个注解在程序运行的时候还能被读取到,而不是用完就丢)
public @interface OperationLog {String moduleName();//模块名String operationType();//操作类型
}
2.在所有controller的方法上,添加该自定义注解
举例:
3.编写切面类OperationLogAspect
@Aspect
@Component
public class OperationLogAspect {//注入SysOperationLogMapper对象@Autowiredprivate SysOperationLogMapper sysOperationLogMapper;//拦截所有加了@OperationLog注解的方法,进行功能无侵入增强(环绕通知)@Around("@annotation(operationLog)")public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {//ProceedingJoinPoint joinPoint:拦截的方法本身(我们要拦截controller层的方法)//OperationLog operationLog:在所拦截的方法上,添加的注解内容//获取当前的HTTP请求对象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//1.获取请求的controller方法的模块名称、操作类型(从注解中获取)String moduleName = operationLog.moduleName();String operationTypeStr = operationLog.operationType();//2.获取本次请求的请求方式(如GET、POST、PUT、DELETE)String requestMethod = request.getMethod();//3.获取请求发起人的用户名称(直接从请求对象中获取即可(别的服务放进去的))//3.1先获取controller方法的参数实体类Object[] args = joinPoint.getArgs();Object entity = args[0];//必须默认controller的实体类参数放在第一个//3.2通过反射,拿到实体类中的用户名称(curUserName)Method getCurUserName = entity.getClass().getMethod("getCurUserName");String operator = (String)getCurUserName.invoke(entity);//4.获取请求发起人的主机ipString hostIp = ClientIpUtil.getClientIpAddress(request);//5.获取操作时间(就是当前的时间)Date operationTime = new Date();//6.先默认操作状态为成功Integer operationStatus = 1;//7.获取请求方法的全类名,如:com.hxb.xltj.controller.SysLogControllerString methodFullName = joinPoint.getTarget().getClass().getName();//System.out.println(methodFullName);//8.被增强的controller方法的返回结果(字符串类型)String methodResult = null;//9.请求路径String requestUrl = request.getRequestURI();//10.请求参数String requestParam = JSON.toJSONString(entity);try{//放行被增强的方法Object result = joinPoint.proceed();//查看被增强方法是否执行成功if(result instanceof ResultBody){ResultBody resultBody = (ResultBody) result;//如果返回的结果响应码不是“000000”,则说明请求失败了,直接令该日志的状态为0(失败)operationStatus = !resultBody.getReturnCode().equals("000000")? 0:1;//如果是“导出”操作,就将返回体ResultBody的data属性设为空(不给前端展示,太长了)if(operationTypeStr.equals("导出")){methodResult = "";}else{//给返回结果赋值methodResult = result.toString();}}//将结果返回出去,不要让切面吞被增强方法的原有逻辑return result;}catch(Exception e){throw e;//继续抛出异常,不影响原有错误处理}finally{//看看都获取到了哪些日志信息//System.out.println(moduleName);//测试模块//System.out.println(operationType);//查询//System.out.println(requestMethod);//POST//System.out.println(hostIp);//127.0.0.1//System.out.println(operationTime);//Thu Aug 21 10:48:07 CST 2025//System.out.println(operationStatus);//1(代表成功)//将操作类型转为整型,再存入数据库Integer operationType = operationTypeStr.equals("新增")?1:operationTypeStr.equals("删除")?2:operationTypeStr.equals("修改")?3:operationTypeStr.equals("查询")?4:operationTypeStr.equals("授权")?5:operationTypeStr.equals("导出")?6:operationTypeStr.equals("导入")?7:operationTypeStr.equals("强退")?8:operationTypeStr.equals("生成代码")?9:operationTypeStr.equals("清空数据")?10:null;//封装系统操作日志记录对象SysOperationLog sysOperationLog = new SysOperationLog(null, moduleName, operationType, requestMethod, operator, hostIp, operationStatus, operationTime,methodFullName,methodResult,requestUrl, requestParam,0,null,null);//将系统操作日志,存入数据库sysOperationLogMapper.insert(sysOperationLog);}}
}
上述代码,核心逻辑就是:获取到【系统日志表】需要的所有字段,然后写入数据库即可。
三.效果展示
1.使用一下管理系统的某个功能
2.查看数据库,肯定会多一条操作日志
四.特别声明
1.如果你想让管理系统的所有行为,都记录到操作日志表中,你需要把后端所有的controller方法,都加上自定义注解@OperatorLog(因为只有这样,才会被AOP切面进行代码无侵入增强)
2.由于AOP切面类OperationLogAspect里面,有一些反射操作(通过controller方法的形参获取操作信息),因此需要团队伙伴在写controller方法时,要遵循一定的约束(比如:controller方法必须通过Body接收参数,且Body参数必须放在形参列表的第一位)。
诸如此类,具体需要团队伙伴遵循哪些约束,需要具体看需求和代码逻辑,我们主要有这个意识就行,其他都好说。
以上就是通过SpringAOP切面编程,实现【记录系统操作日志】功能的详细流程,喜欢的话可以留个免费的关注呦~~~