spring AOP实现后端日志操作记录功能

2023-10-30

一、引入配置文件

     <!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- guava工具包 -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.0-jre</version>
        </dependency>

二、编写对应的枚举类

package com.baizhi.enus;
 
/**
 * @author xxs
 * @date 2022年08月10日 16:44
 */
public interface OpType {
 
    /**
     * 新增
     */
    public static final int ADD = 1;
 
    /**
     * 修改
     */
    public static final int UPDATE = 2;
    /**
     * 删除
     */
    public static final int DELETE = 3;
    /**
     * 查询-分页
     */
    public static final int QUERY_PAGE = 4;
    /**
     * 其他
     */
    public static final int OTHER = 5;
    /**
     * 登录
     */
    public static final int LOGIN = 6;
    /**
     * 登出
     */
    public static final int LOGOUT = 7;
    /**
     * 导出
     */
    public static final int EXPORT = 8;
    /**
     * 导入
     */
    public static final int IMPORT = 9;
    /**
     * 保存
     */
    public static final int SAVE = 10;
    /**
     * 发送邮件
     */
    public static final int SEND_EMAIL = 11;
    /**
     * 发送短信
     */
    public static final int SMS = 12;
    /**
     * 查询-不分页
     */
    public static final int QUERY_NO_PAGE = 13;
    /**
     * 查询明细
     */
    public static final int QUERY_DETAIL = 14;
    /**
     * 审核
     */
    public static final int AUDIT = 15;
    /**
     * 反审核
     */
    public static final int UN_AUDIT = 16;
    /**
     * 下载
     */
    public static final int DOWNLOAD = 17;
    /**
     * 上传
     */
    public static final int UPLOAD = 18;
    /**
     * 校验
     */
    public static final int CHECK = 19;
    /**
     * 安装
     */
    public static final int INSTALL = 20;
    /**
     * 卸载
     */
    public static final int UN_INSTALL = 21;
    /**
     * 启动
     */
    public static final int START = 22;
    /**
     * 停止
     */
    public static final int STOP = 23;
    /**
     * 重启
     */
    public static final int RESTART = 24;
    /**
     * 暂停
     */
    public static final int PAUSE = 25;
    /**
     * 恢复
     */
    public static final int RESUME = 26;
    /**
     * 刷新
     */
    public static final int REFRESH = 27;
    /**
     * 生成
     */
    public static final int GENERATE = 28;
    /**
     * 统计
     */
    public static final int STATISTICS = 29;
 
    /**
     * 同步
     */
    public static final int SYNC = 30;
    /**
     * 定时调度
     */
    public static final int SCHEDULE = 31;
 
 
 
}
package com.baizhi.enus;
 
import java.util.ArrayList;
import java.util.List;
 
/**
 * @author hz
 */
public enum OpTypeEnum {
    /*
        1. 增加
        2. 删除
        3. 修改
        4. 查询
        5. 其他
     */
    ADD(OpType.ADD, "新增"),
 
    UPDATE(OpType.UPDATE, "修改"),
 
    DELETE(OpType.DELETE, "删除"),
 
    QUERY_PAGE(OpType.QUERY_PAGE, "查询-分页", true),
 
    OTHER(OpType.OTHER, "其他"),
 
    LOGIN(OpType.LOGIN, "登录"),
 
    LOGOUT(OpType.LOGOUT, "登出"),
 
    EXPORT(OpType.EXPORT, "导出"),
 
    IMPORT(OpType.IMPORT, "导入"),
 
    SAVE(OpType.SAVE, "保存"),
 
    SEND_EMAIL(OpType.SEND_EMAIL, "发送邮件"),
 
    SMS(OpType.SMS, "发送短信"),
 
    QUERY_NO_PAGE(OpType.QUERY_NO_PAGE, "查询-不分页", true),
 
    QUERY_DETAIL(OpType.QUERY_DETAIL, "查询明细", true),
 
    AUDIT(OpType.AUDIT, "审核"),
 
    UN_AUDIT(OpType.UN_AUDIT, "反审核"),
 
    DOWNLOAD(OpType.DOWNLOAD, "下载"),
 
    UPLOAD(OpType.UPLOAD, "上传"),
 
    CHECK(OpType.CHECK, "校验"),
 
    INSTALL(OpType.INSTALL, "安装"),
 
    UN_INSTALL(OpType.UN_INSTALL, "卸载"),
 
    START(OpType.START, "启动"),
 
    STOP(OpType.STOP, "停止"),
 
    RESTART(OpType.RESTART, "重启"),
 
    PAUSE(OpType.PAUSE, "暂停"),
 
    RESUME(OpType.RESUME, "恢复"),
 
    REFRESH(OpType.REFRESH, "刷新"),
 
    GENERATE(OpType.GENERATE, "生成"),
 
    STATISTICS(OpType.STATISTICS, "统计"),
 
    SYNC(OpType.SYNC, "同步"),
 
    SCHEDULE(OpType.SCHEDULE, "定时调度"),
 
    last(0, "最后一个");
    final int key;
    final String desc;
    //是否忽略返回结果
    final boolean ignoreResult;
 
    OpTypeEnum(int key, String desc) {
        this.key = key;
        this.desc = desc;
        this.ignoreResult = false;
    }
 
    OpTypeEnum(int key, String desc, boolean ignoreResult) {
        this.key = key;
        this.desc = desc;
        this.ignoreResult = ignoreResult;
    }
 
    /**
     * 根据key获取desc
     */
    public static String getDesc(Integer key) {
        for (OpTypeEnum opTypeEnum : OpTypeEnum.values()) {
            if (opTypeEnum.getKey() == key) {
                return opTypeEnum.getDesc();
            }
        }
        return "";
    }
 
    /**
     * 获取所有忽略返回结果的Key集合
     */
    public static List<Integer> getIgnoreResultKeys() {
        List<Integer> keys = new ArrayList<>();
        for (OpTypeEnum opTypeEnum : OpTypeEnum.values()) {
            if (opTypeEnum.isIgnoreResult()) {
                keys.add(opTypeEnum.getKey());
            }
        }
        return keys;
    }
 
    public int getKey() {
        return key;
    }
 
    public String getDesc() {
        return desc;
    }
 
    public boolean isIgnoreResult() {
        return ignoreResult;
    }
 
}

三、这里我使用的是注解的形式进行的切面

package com.baizhi.annotation;

import com.baizhi.enus.OpType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER,ElementType.METHOD})
public @interface OptLog {
    /**
     * 业务类型,如新增、删除、修改
     * 默认 其他
     * @return
     */

    int opType() default OpType.OTHER;

    /**
     * 业务对象名称,如订单、库存、价格
     *
     * @return
     */
     String opItem();

    /**
     * 业务对象编号表达式,描述了如何获取订单号的表达式
     *
     * @return
     */
     String opItemIdExpression();
    /**
    *是否使用注解切面默认是使用的注解切面
    **/

    boolean isSaveRequestData() default true;


}

四、编写AOP

package com.baizhi.aop;

import com.baizhi.annotation.OptLog;
import com.baizhi.enus.OpTypeEnum;
import io.micrometer.core.instrument.util.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import com.google.common.base.CaseFormat;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Optional;


@Aspect
@Component
public class LogMethodAspect {

    private static final Logger log = LoggerFactory.getLogger(LogMethodAspect.class);

    /**
     * 指定切面类的位置*/
    @Pointcut("@annotation(com.baizhi.annotation.OptLog)")
    public void myAnnotationAspect(){}
        @Autowired
        HttpServletRequest request;

        @Around("myAnnotationAspect()")
        public Object log(ProceedingJoinPoint pjp) throws Exception {

            Method method = ((MethodSignature)pjp.getSignature()).getMethod();
            OptLog opLog = method.getAnnotation(OptLog.class);

            Object response = null;

            try {
                // 目标方法执行
                response = pjp.proceed();
            } catch (Throwable throwable) {
                throw new Exception(throwable);
            }

            if (StringUtils.isNotEmpty(opLog.opItemIdExpression()) && opLog.isSaveRequestData()) {
                SpelExpressionParser parser = new SpelExpressionParser();
                Expression expression = parser.parseExpression(opLog.opItemIdExpression());

                EvaluationContext context = new StandardEvaluationContext();
                // 获取参数值
                Object[] args = pjp.getArgs();

                // 获取运行时参数的名称
                LocalVariableTableParameterNameDiscoverer discoverer
                        = new LocalVariableTableParameterNameDiscoverer();
                String[] parameterNames = discoverer.getParameterNames(method);

                // 将参数绑定到context中
                if (parameterNames != null) {
                    for (int i = 0; i < parameterNames.length; i++) {
                        context.setVariable(parameterNames[i], args[i]);
                    }
                }

                // 将方法的resp当做变量放到context中,变量名称为该类名转化为小写字母开头的驼峰形式
                if (response != null) {
                    context.setVariable(
                            CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, response.getClass().getSimpleName()),
                            response);
                }

                // 解析表达式,获取结果
                String itemId = String.valueOf(expression.getValue(context));
                //TODO 缺少登录用户id
                // 执行日志记录
                handle(opLog.opType(), opLog.opItem(), itemId);
            }

            return response;
        }

//这里模拟的是数据库的操作插入对应的log表执行插入操作。我这里的插入人还没有如果大写写的话建议在新增一个插入人员的id方便我们后期对log表查询的时候知道插入的人的,人员信息以便我们项目对日志有着很好的管理
        private void handle(Integer opType,  String opItem, String opItemId) {
            //
            String orElse= Optional.ofNullable(OpTypeEnum.getDesc(opType)).orElse("");
            // 通过日志打印输出
            log.info("opType = " + opItem +",opItem = " +orElse + ",opItemId = " +opItemId);
        }
    }

 五、编写controller测试

  /**
     * 编辑数据
     *
     * @param log 实体
     * @return 编辑结果
     */

//opItemIdExpression = "#log.id" 这里接收的值是根据前台传入的值确定的 相当于记录的管理员或者其//他人员操作的唯一id方便我们后期对日志可以进行有效的排查。
//如果为get请求?或者是/ 接收的后台参数直接拿 opItemIdExpression = "#id" 进行接收


    @OptLog(opType = OpType.UPDATE, opItem = "log查询", opItemIdExpression = "#log.id", isSaveRequestData = true)
    @PutMapping
    public ResponseEntity<Log> edit(@RequestBody Log log) {
        return ResponseEntity.ok(this.logService.update(log));
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring AOP实现后端日志操作记录功能 的相关文章

  • java.sql.SQLException: ORA-01005: 给定的密码为空;登录被拒绝

    我在尝试连接到数据库时遇到以下异常 java sql SQLException ORA 01005 null password given logon denied at oracle jdbc driver T4CTTIoer proce
  • 无法解析配置“:app:debugRuntimeClasspath”的所有文件。问题

    我的 android studio 遇到了下一个问题 导致 org gradle api internal artifacts ivyservice DefaultLenientConfiguration ArtifactResolveEx
  • CSS 未在 Spring Boot 中加载

    我是 spring 框架工作和 spring boot 的新手 我正在尝试使用 CSS javascript js 添加静态 html 文件 文件结构是 我的 html 文件头看起来像这样
  • @NotNull.List 的目的

    当我查看标准时限制条件 http docs oracle com javaee 6 api javax validation constraints package summary html在 Bean Validation API JSR
  • PrintStream是有缓冲的,但是flush不会降低性能,而BufferedOutputStream会加速性能

    我预计由于 PrintStream 是缓冲的 通过在每次 print 之后添加刷新操作 速度性能应该会显着降低 但事实并非如此 如下面的代码片段所示 此外 将 PrintStream 包裹在 BufferedOutputStream 周围可
  • 文件保存在文件系统中 VS 保存在数据库中

    我正在设计一个 servlet 或 Struts2 中的操作 用于文件 图像 文档等 下载 但我想知道哪种更好的方法可以将文件保留在文件系统和数据库中 只需保留文件的路径或将文件保留在数据库中 如 BLOB 我知道当我查询数据库时 哪里的
  • 可以混合使用 JVM 语言吗?即:Groovy 和 Clojure

    我知道你可以轻松地混合groovy java clojure java 无论什么JvmLang java 这是否也意味着我也可以让 clojure 和 groovy 代码进行交互 如果我使用 Grails 或 jRoR 我也可以在该环境中使
  • Keycloak 社交登录 REST API

    我已经为我的 keycloak 实例启用了谷歌社交登录 但我需要将其用作休息服务 是否有可用于执行此操作的端点 Keycloak 中没有 Google 身份验证 API 但您可以使用以下方法解决它代币交换 https www keycloa
  • 如何连接hibernate和DB2

    我正在运行一个使用 struts 和 hibernate 的应用程序 我目前正在使用 Derby 数据库 现在我必须转向 DB2 数据库 请告诉我 我必须做什么配置 休眠配置文件 我必须设置任何类路径吗 多变的 我知道 DB2 有两个 ja
  • 在 javafx 中注册鼠标处理程序,但处理程序不是内联的

    我有一个 JavaFX 应用程序变得有点大 我想保持代码的可读性 我有一个折线图 我希望内置缩放功能 该功能在单击鼠标时发生 我知道我需要向图表注册鼠标侦听器 我无法从 Oracle 示例中弄清楚什么 即如下所示 http docs ora
  • 如何在 Ivy 中使用不同的分类器下载多个 Maven 依赖项?

    我试图依靠Neo4j 服务器 jar http repo neo4j org content repositories snapshots org neo4j app neo4j server 1 5 SNAPSHOT neo4j serv
  • 将传入字符串的 unicode 表示形式转换为 UTF-8?

    我正在读取一些已经转换为 html 样式 代码的数据 我现在需要将其转换回 UTF 8 字符以供查看 不幸的是我无法使用浏览器查看该字符串 我读过有关 java 中的转换的内容 似乎如果你有一个 uxxxx 字符串 那么编译器会为你转换 然
  • 在 Java 中打开现有文件并关闭它。

    是否可以在java中打开一个文件附加数据并关闭多次 例如 psuedocode class variable declaration FileWriter writer1 new FileWriter filename fn1 writer
  • 获取证书链

    我正在 Java 中使用 X509 证书 给定一个证书 是否可以在签名层次结构中找到所有其他证书 直到找到根证书 我有一个证书文件 带有 cer扩展名 我想提取父签名证书 我想继续查找该证书的父证书 直到获得最终的自签名根证书 我已经检查了
  • Spring Boot:在映射级别指定端口

    Spring Boot 我希望实现以下目标 一些 URL 路径映射到一个端口 一些映射到另一个端口 换句话说 我想要这样的东西 public class Controller1 RequestMapping value path1 port
  • Maven `help: effective-pom` 只为单个项目生成,而不是所有项目

    我想为多模块构建中的所有子项目生成有效的 pom The help effective pom文档here http maven apache org plugins maven help plugin usage html The hel
  • Android应用程序中的模式输入

    我想知道是否有其他替代方案可以替代 Android 上平庸的 EditText 密码输入 是否有 API 或开源代码可以集成到我的应用程序中 类似于锁屏图案解锁 Intent 可能会返回哈希值 数字 字符串或代表用户输入的模式的任何内容 我
  • RecyclerView 适配器的 Kotlin 泛型

    我正在尝试编写一个通用的 recyclerview 适配器 我找到了几个例子 然而 仍然无法弄清楚如何实现通用适配器 我写的代码是 open abstract class BaseAdapter
  • 如何正确使用Google Calendar API Events.Insert命令?

    所以我一直使用REST方法来调用Google的API 我需要将事件插入到我拥有 ID 的特定日历中 这是我发送的 POST 请求 地址 https www googleapis com calendar v3 calendars https
  • javafx中的stackpane和root有什么区别?

    我正在练习javafx做饼图 以下是开发饼图的代码 如果我这样做Group并与StackPane 我发现输出没有区别 我已经评论了组部分 只是徘徊两者之间的区别 import javafx application Application i

随机推荐

  • Java IDEA辅助键和快捷键

    快速生成main方法 psvm 回车 快速生成输出语句 sout 回车 内容辅助键 Ctrl Alt space 内容提示 代码补全等 格式化 Ctrl Alt L
  • java nio 编程

    转自 http yangguangfu iteye com blog 774194 Java代码 晚上学习了下Java 的 NIO Socket编程 写了下面这个小程序 包括服务器端与客户端 实现的功能为客户端向服务器端发送随即数目的消息
  • Java Executors(线程池)

    Sun在Java5中 对Java线程的类库做了大量的扩展 其中线程池就是Java5的新特征之一 除了线程池之外 还有很多多线程相关的内容 为多线程的编程带来了极大便利 为了编写高效稳定可靠的多线程程序 线程部分的新增内容显得尤为重要 有关J
  • PHP学习记录--基础篇

    目录 一 变量定义 二 循环结构 三 函数 四 文件加载原理 五 错误处理 六 字符串 七 数组 一 变量定义
  • C进阶之实现数据库(mysql 5.0)版本通讯录

    前言 断断续续历时两天百度的知识探索 继静态 动态 文件版本的通讯录之后 倔强的我开启了数据库版本通讯录 在动态版本通讯录的基础上将通讯人信息数据插入数据库中 初始化通讯录完成后 再从数据库中查询通讯人信息数据 的痛苦长路 想说脏话但是稍微
  • 在九天毕昇平台运行paddle出现fatal error: ‘Segmentation fault‘ is detected by the operating system

    我在九天毕昇平台使用paddlepaddle框架训练实例时 调用GPU出现下文错误 C Traceback most recent call last No stack trace in paddle may be caused by ex
  • SQL查询结果去重

    SQL查询结果去重 使用distinct关键字 去除重复的记录行 SELECT loc FROM dept SELECT DISTINCT loc FROM dept 案例 描述 题目 现在运营需要查看用户来自于哪些学校 请从用户信息表中取
  • opengl 光线追踪_基于CUDA的GPU光线追踪

    先放最终效果 实时光线追踪https www zhihu com video 1073144630408941568 十天前我打算用我之前的CPU渲染器渲染一张博客封面 大概是这张 我之前的渲染器是使用C 写的 已经进行过多线程优化 但是为
  • H5页面怎么跳转到公众号主页?看过来

    前言 做公众号开发的小伙伴 可能会遇到这种需求 在一个H5页面点击一个关注公众号按钮跳转到公众号主页 听到这个需求的一瞬间 疑惑了 这不可能 摸了摸高亮的额头 没办法 做还是要做的 开始上解决方案 方案一 自己做一个关注公众号的引导页面 让
  • 【Node.js项目】大事件项目:后台架构图(含具体技术栈)、典型代码

    项目学习自 https www bilibili com video BV1a34y167AZ 文章目录 1 项目后台架构图 含具体技术栈 2 项目目录结构 3 项目典型代码 config config js app js schema u
  • docker进阶笔记(高级版)

    1 mysql主从复制 mysql master 新建mysql master容器 docker run d p 3307 3306 privileged true v Users yaoqiang app data mysql maste
  • Unity:Quaternion类(四元数)

    Unity Quaternion 四元数 一 四元数理解 四元数左乘向量 表示将该向量按照四元数表示的角度旋转 例如 Vector3 point new Vector3 0 0 10 Vector3 newPoint Quaternion
  • 微信小程序中图片占满整个屏幕实现方法

    将body和html设置为100 这样我们就可以在他们的子元素中使用height 100 来使的我们的容器元素占满屏幕的高度啦 但是在微信小程序中 是没有dom对象的 根节点是page 使用page height 100 果然 是可行的 高
  • C/C++实现快速排序(两种方式)

    介绍 快速排序是对冒泡排序算法的一种改进 快速排序算法通过多次比较和交换来实现排序 流程如下 图片来自百度 实现 以下有两种实现方式 说是两种 其实就是在交换元素时具体细节上有点不同罢了 方式一 int Partition int A in
  • Spring的RestTemplate是什么东西?它有什么作用?

    RestTemplate是Spring提供的一个服务之间请求的工具 并且可以自动实现json的序列化和反序列化
  • pycharm导包不成功,如何清缓存,设置源目录

    pycharm导入本地包不成功 解决方法 1 清缓存 2 pycharm设置源目录
  • 中心化(又叫零均值化)和标准化(又叫归一化)

    一 中心化 又叫零均值化 和标准化 又叫归一化 概念及目的 1 在回归问题和一些机器学习算法中 以及训练神经网络的过程中 通常需要对原始数据进行中心化 Zero centered或者Mean subtraction subtraction表
  • pid双闭环matlab仿真,SPWM波控制单相逆变器双闭环PID调节器的Simulink建模与仿真

    随着电力行业的快速发展 逆变器的应用越来越广泛 逆变器的好坏会直接影响整个系统的逆变性能和带载能力 逆变器的控制目标是提高逆变器输出电压的稳态和动态性能 稳态性能主要是指输出电压的稳态精度和提高带不平衡负载的能力 动态性能主要是指输出电压的
  • 计算机网络复习-习题整理(三) 物理层

    1 对于带宽为4KHz的信道 若用8种不同的物理状态来表示数据 信噪比为30db 试问按奈奎斯特定理 最大限制的数据速率是多少 按香农定理最大限制的数据速率是多少 正确答案 按奈奎斯特定理 C 2W log2N N 8 W 4KHZ C 2
  • spring AOP实现后端日志操作记录功能

    一 引入配置文件