自定义注解+AOP实现日志记录

定义日志表结构

CREATE TABLE `sys_oper_log` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',`title` varchar(50) DEFAULT '' COMMENT '模块标题',`business_type` varchar(20) DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',`method` varchar(100) DEFAULT '' COMMENT '方法名称',`request_method` varchar(10) DEFAULT '' COMMENT '请求方式',`operator_type` varchar(20) DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',`oper_name` varchar(50) DEFAULT '' COMMENT '操作人员',`dept_name` varchar(50) DEFAULT '' COMMENT '部门名称',`oper_url` varchar(255) DEFAULT '' COMMENT '请求URL',`oper_ip` varchar(128) DEFAULT '' COMMENT '主机地址',`oper_param` varchar(2000) DEFAULT '' COMMENT '请求参数',`json_result` varchar(2000) DEFAULT '' COMMENT '返回参数',`status` int DEFAULT '0' COMMENT '操作状态(0正常 1异常)',`error_msg` varchar(2000) DEFAULT '' COMMENT '错误消息',`oper_time` datetime DEFAULT NULL COMMENT '操作时间',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:不可用 1:可用)',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8mb3 COMMENT='操作日志记录';

切面类环境搭建

依赖导入

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

自定义Log注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {		// 自定义操作日志记录注解public String title() ;								// 模块名称public OperatorType operatorType() default OperatorType.MANAGE;	// 操作人类别public int businessType() ;     // 业务类型(0其它 1新增 2修改 3删除)public boolean isSaveRequestData() default true;   // 是否保存请求的参数public boolean isSaveResponseData() default true;  // 是否保存响应的参数}

 操作人枚举类定义

public enum OperatorType {		// 操作人类别OTHER,		// 其他MANAGE,		// 后台用户MOBILE		// 手机端用户
}

日志切面类

@Aspect
@Component
@Slf4j
public class LogAspect {            // 环绕通知切面类定义@Autowiredprivate AsyncOperLogService asyncOperLogService ;@Around(value = "@annotation(sysLog)")public Object doAroundAdvice(ProceedingJoinPoint joinPoint , Log sysLog) {// 构建前置参数SysOperLog sysOperLog = new SysOperLog() ;LogUtil.beforeHandleLog(sysLog , joinPoint , sysOperLog) ;Object proceed = null;try {proceed = joinPoint.proceed();// 执行业务方法LogUtil.afterHandlLog(sysLog , proceed , sysOperLog , 0 , null) ;// 构建响应结果参数} catch (Throwable e) {                                 // 代码执行进入到catch中,// 业务方法执行产生异常e.printStackTrace();                                // 打印异常信息LogUtil.afterHandlLog(sysLog , proceed , sysOperLog , 1 , e.getMessage()) ;throw new RuntimeException();}// 保存日志数据asyncOperLogService.saveSysOperLog(sysOperLog);// 返回执行结果return proceed ;}
}

 自定义注解实现切面类使用

@Target({ElementType.TYPE})        //作用在类上
@Retention(RetentionPolicy.RUNTIME) //被修饰的注解的保留策略 在运行时依然可以通过反射得到注解中的信息
@Import(value = LogAspect.class)            // 通过Import注解导入日志切面类到Spring容器中
public @interface EnableLogAspect {}

在启动类上添加@EnableLogAspect注解

在要添加日志功能的业务接口方法上添加Log注解,启动服务进行测试

@Log(title = "角色添加",businessType = 0) //添加Log注解,设置属性
@PostMapping(value = "/saveSysRole")
public Result saveSysRole(@RequestBody SysRole SysRole) {sysRoleService.saveSysRole(SysRole) ;return Result.build(null , ResultCodeEnum.SUCCESS) ;
}

保存日志功能

定义一个与日志表相同的实体类

@Data
public class SysOperLog extends BaseEntity {private String title;					// 模块标题private String method;					// 方法名称private String requestMethod;			// 请求方式private String operatorType;			// 操作类别(0其它 1后台用户 2手机端用户)private Integer businessType ;			// 业务类型(0其它 1新增 2修改 3删除)private String operName;				// 操作人员private String operUrl;					// 请求URLprivate String operIp;					// 主机地址private String operParam;				// 请求参数private String jsonResult;				// 返回参数private Integer status;					// 操作状态(0正常 1异常)private String errorMsg;				// 错误消息}

定义日志记录切面工具类 

日志切面类调用工具

public class LogUtil {//操作执行之后调用public static void afterHandlLog(Log sysLog, Object proceed,SysOperLog sysOperLog, int status ,String errorMsg) {if(sysLog.isSaveResponseData()) {sysOperLog.setJsonResult(JSON.toJSONString(proceed));}sysOperLog.setStatus(status);sysOperLog.setErrorMsg(errorMsg);}//操作执行之前调用public static void beforeHandleLog(Log sysLog,ProceedingJoinPoint joinPoint,SysOperLog sysOperLog) {// 设置操作模块名称sysOperLog.setTitle(sysLog.title());sysOperLog.setOperatorType(sysLog.operatorType().name());// 获取目标方法信息MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature() ;Method method = methodSignature.getMethod();sysOperLog.setMethod(method.getDeclaringClass().getName());// 获取请求相关参数ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();sysOperLog.setRequestMethod(request.getMethod());sysOperLog.setOperUrl(request.getRequestURI());sysOperLog.setOperIp(request.getRemoteAddr());// 设置请求参数if(sysLog.isSaveRequestData()) {String requestMethod = sysOperLog.getRequestMethod();if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {String params = Arrays.toString(joinPoint.getArgs());sysOperLog.setOperParam(params);}}sysOperLog.setOperName(AuthContextUtil.get().getUserName());}
}

 定义保存日志数据的service接口

//接口类
public interface AsyncOperLogService {			// 保存日志数据public abstract void saveSysOperLog(SysOperLog sysOperLog) ;
}//实现类
@Service
public class AsyncOperLogServiceImpl implements AsyncOperLogService {@Autowiredprivate SysOperLogMapper sysOperLogMapper;@Async      // 异步执行保存日志操作@Overridepublic void saveSysOperLog(SysOperLog sysOperLog) {sysOperLogMapper.insert(sysOperLog);}}

持久层接口

@Mapper
public interface SysOperLogMapper {public abstract void insert(SysOperLog sysOperLog);
}

 映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.demo.spzx.mapper.SysOperLogMapper"><insert id="insert" >insert into sys_oper_log (id,title,method,request_method,operator_type,oper_name,oper_url,oper_ip,oper_param,json_result,status,error_msg) values (#{id},#{title},#{method},#{requestMethod},#{operatorType},#{operName},#{operUrl},#{operIp},#{operParam},#{jsonResult},#{status},#{errorMsg})</insert></mapper>

在需要添加操作日志的接口方法上添加@Log注解进行测试。

@Log(title = "品牌列表",businessType = 0,operatorType = OperatorType.MANAGE)
//品牌列表(分页)
@GetMapping("/{page}/{limit}")
public Result list(@PathVariable Integer page,@PathVariable Integer limit) {PageInfo<Brand> pageInfo = brandService.list(page,limit);return Result.build(pageInfo,ResultCodeEnum.SUCCESS);
}

事务失效问题的解决

问题分析

Spring的事务控制是通过aop进行实现的,在框架底层会存在一个事务切面类,当业务方法产生异常以后,事务切面类感知到异常以后事务进行回滚。

当系统中存在多个切面类的时候,Spring框架会按照@Order注解的值对切面进行排序,@Order的值越小优先级越高,@Order的值越大优先级越低。优先级越高的切面类越优先执行,当我们没有给切面类指定排序值的时候,我们自定义的切面类的优先级和aop切面类的优先级相同,那么此时事务切面类的优先级要高于自定义切面类,那么切面类的执行顺序如下所示:

当在自定义切面类中对异常进行了捕获,没有将异常进行抛出,那么此时事务切面类是感知不到异常的存在,因此事务失效。

问题解决

解决方案一:使用@Order注解提高自定义切面类的优先级

解决方案二:在自定义切面类的catch中进行异常的抛出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/1551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

9.MMD 基础内容总结及制作成品流程

前期准备 1. 导入场景和模型 在左上角菜单栏&#xff0c;显示里将编辑模型时保持相机和光照勾选上&#xff0c;有助于后期调色 将抗锯齿和各向异性过滤勾掉&#xff0c;可以节省资源&#xff0c;避免bug 在分辨率设定窗口&#xff0c;可以调整分辨率 3840x2160 4k分辨率 1…

【python】图形用户界面学习之tkinter

认识tkinter Tkinter是Python中内置的图形用户界面&#xff08;GUI&#xff09;库。它是Tk GUI工具包的接口&#xff0c;可以创建和管理窗口、按钮、标签、文本框等各种GUI组件&#xff0c;并与用户交互。 使用Tkinter&#xff0c;可以创建各种GUI应用程序&#xff0c;如桌面…

QT Webengine开发过程报错qml: Render process exited with code 159 (killed)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、解决方法二、补充说明总结 前言 基于QT的Webengine开发过程中&#xff0c;QT的官方示例quicknanobrowser也无法成功运行&#xff0c;程序运行后&#xff0…

git的学习

设置用户&#xff08;目的在于可以在远端看到是谁提交了内容&#xff0c;更方便公司的管理&#xff09; 1、设置全局用户&#xff08;在家路径下创建用户&#xff0c;每个项目都用这一个用户&#xff09;>: git config --global user.name manba >: git config --global …

《AI聊天类工具之八——​ 小悟空》

一.简介 官网:小悟空 小悟空是一款集智能对话和辅助推荐功能于一体的强大工具集。它为用户提供了创作生成、学习提升、工作职场、专业咨询、虚拟角色和休闲娱乐等二十余个类别的智能服务,以满足用户在工作、生活和娱乐各方面的需求。 在创作生成方面,小悟空可以帮助用户进…

使用Go语言和chromedp库下载Instagram图片:简易指南

摘要/导言 本文将介绍如何使用Go语言配合chromedp库来下载Instagram上的图片。我们将通过一个简单的示例来展示整个过程&#xff0c;包括如何设置爬虫代理IP以绕过网络限制。 背景/引言 在数据采集和自动化测试领域&#xff0c;Go语言以其出色的执行效率、简洁的语法和卓越的…

【科学研究】那些考进精英大学的农家子弟们

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

智能化转型的得力助手:山海鲸智慧工厂解决方案详解

在数字化浪潮席卷全球的今天&#xff0c;工业领域正迎来一场前所未有的智能化变革。作为这一变革的领军者&#xff0c;山海鲸智慧工厂解决方案以其前瞻性的技术理念和创新的解决方案&#xff0c;为工业发展注入了强大的动力。 山海鲸智慧工厂解决方案的核心在于其高度的集成性…

uniapp读取(获取)缓存中的对象值(微信小程序)

文章目录 问题描述解决方案存值取值 问题描述 大家好&#xff01;我是夏小花&#xff0c;今天是2024年4月24日|农历三月十六&#xff0c;在我们日常开发中&#xff0c;做小程序可能会往缓存中存值或者是存对象&#xff0c;今天这篇博客文章主要是讲如何在微信小程序如何读取缓存…

Windows Server 2012 R2 中 IIS 8.5 安装证书

文章目录 前言一、获取服务器证书二、证书格式转换二、IIS8安装证书1.Win R 键打开运行窗口 → 输入【inetmgr】→ 点击【确定】2.打开【IIS管理器】→ 点击计算机名称 → 双击打开【服务器证书】3.点击【导入】4.选择证书文件 → 输入密码 → 点击【确定】5.选择要使用证书的…

14. Spring AOP(二)实现原理

源码位置&#xff1a;spring_aop 上一篇文章中我们主要学习了AOP的思想和Spring AOP使用&#xff0c;本文讲的是Spring是如何实现AOP的&#xff0c;Spring AOP是基于动态代理来实现AOP的&#xff0c;在将动态代理之前先来了解一下什么是代理模式。 1. 代理模式 在现实中就有许…

深度学习transformer架构详细详解

一、transformer的贡献 transformer架构的贡献&#xff1a;该架构只使用自注意力机制&#xff0c;没有使用RNN或卷积网络。且可以实现并行计算&#xff0c;加快模型训练速度。 &#xff08;将所有的循环层全部换成&#xff1a;multi-headed self-attention&#xff09; 二、t…

VisualStudio2019和2022开发Winform项目用到Devexpress组件报错不能正确加载的解决办法

1.报错1 问题简单描述&#xff1a;DevExpress.Utils.ImageCollectionStreamer 无法强制转换为 DevExpress.Utils.ImageCollectionStreamer。 原因分析&#xff1a;原项目某个组件使用的是 DevExpresss.XtraBars.v15.1版本&#xff0c;直接引用扩展控件改成引用v20.2。 解决办法…

负载均衡集群——Nginx

目录 1.Nginx反向代理实战 2.Nginx 反向代理和负载均衡实践 实验操作步骤 步骤 1 Nginx1 和 Nginx2 配置 步骤2 测试资源是否可用 步骤 3 安装和配置 Nginx 代理 步骤 4 代理服务器配置检测 步骤 5 在 Nginx1 和 Nginx2 配置虚拟主机 步骤 6 将虚拟主机添加入后端主机组…

Let’s Encrypt 申请免费https证书(snapd安装)

Let’s Encrypt 最近给域名安装免费的https证书 Let’s Encrypt&#xff0c;发现跟之前的安装方式不太一样&#xff0c;这里记录一下安装过程 https://certbot.eff.org/instructions?wsnginx&oscentosrhel7 https://eff-certbot.readthedocs.io/en/latest/using.html#ng…

BMR:基于Boostrapping多视图的虚假新闻检测

一、概述 文章提出了三种视图信息来表示一篇新闻&#xff1a;文本、图像结构、图像语义。然后设计了改进的多门混合专家系统&#xff08;iMMoE&#xff09;来进行信息融合。保留单模态信息来保证特征对新闻的保真性&#xff0c;增加的多模态信息能保证不同模态的一致性&#xf…

【技术】Spring Boot 将 Word 转换为 PDF 2.0 版本

之前写过一篇 Spring Boot 将 Word 转换为 PDF 的文章&#xff0c;但是有评论说导入依赖有问题&#xff0c;还存在依赖冲突的问题。索性再来一个完整版的代码&#xff0c;之前的完整版代码找不到了&#xff0c;又重新整理了一下&#xff0c;依赖导入和之前不太一样&#xff0c;…

【KingSCADA】通过地址引用和弹窗模板实现设备控制

当相同的设备过多时&#xff0c;要做很多相同的弹窗&#xff0c;这种情况下可以通过地址引用和弹窗模板实现设备控制。 1.变量创建 2.画面开发 以阀门控制为例&#xff0c;只需要做一个阀门控制界面模板 3.地址引用 # 4.实现效果

eCognition 样本标注与导出

目录 一、可导出shp的样本标注方式 1、选择样本 2、导出样本shp 3、附录:转成样本用于训练 二、只能导出tif,不可导出shp 1、打开样本工具栏 2、选择样本 3、导出标注

每日算法4/21

LCR 073. 爱吃香蕉的狒狒 题目 狒狒喜欢吃香蕉。这里有 N 堆香蕉&#xff0c;第 i 堆中有 piles[i] 根香蕉。警卫已经离开了&#xff0c;将在 H 小时后回来。 狒狒可以决定她吃香蕉的速度 K &#xff08;单位&#xff1a;根/小时&#xff09;。每个小时&#xff0c;她将会选…