【Spring】AOP记录日志

news2024/11/27 8:36:43

我的aop记录日志,可以记录:【 操作类型、操作描述、参数、登录项目的用户ip】 当然记录什么靠你自己决定。

一.自定义一个注解

@Target({ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopLog {

    //操作描述
    String description() default "";

    //操作类型
    String operateType() default "";
    
}

二.定义aop记录日志工具类

  • @Before: 前置通知, 在方法执行之前执行。
  • @After: 后置通知, 在方法执行之后执行 。
  • @AfterRunning: 返回通知, 在方法返回结果之后执行。
  • @AfterThrowing: 异常通知, 在方法抛出异常之后。
  • @Around: 环绕通知, 围绕着方法执行、

@Aspect
@Component
public class LogAspectUtil  {

	//这是我自己的接口实现类,就是存入我的数据库需要的接口
    @Autowired
    private PsLogServiceImpl psLogService; 

    /**
     * service层切点
     */
    @Pointcut("@annotation(com.navi.vpx.common.util.AopLog)") //这里是你自定义注解的位置
    public void serviceAspect() {
    }

    /**
     * 记录日志,写入数据库
     * @param joinPoint 切入点参数
     */
    @After("serviceAspect()")//这里设置的是当你的方法执行完之后,才会执行该方法,也可以使用上面不同的注解在不同时间进行记录。
    public void doServiceLog(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        
        //这里是获取登录你项目用户的ip地址
        try { 
            String unknown = "unknown";
//            String ip0 = request.getHeader("x-forwarded-for");
            String ip = request.getHeader("X-Real-IP");

            if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }

            if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }

            if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }

//            if (LOCAL_IP.equals(ip)) {
//                ip = "local";
//            }

			//将记录写入数据库
            PsLog psLog = new PsLog();
            psLog.setCreateTime(new Date());//设置当前时间
            psLog.setDes( "操作描述:" + getDes(joinPoint) + "   操作类型:" + getOperateType(joinPoint) );//操作描述和操作类型
            psLog.setArgs(getArgs(joinPoint));//设置参数
            psLog.setIp(ip);//设置当前登录用户ip
            psLogService.insertLog(psLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取方法的描述信息
     * @param joinPoint
     * @return
     */
    private String getDes(JoinPoint joinPoint) throws ClassNotFoundException {
        //获取被代理对象名称
        String targetName = joinPoint.getTarget().getClass().getName();
        //获取目标方法名称
        String methodName = joinPoint.getSignature().getName();
        //获取传入目标方法的参数对象
        Object[] arguments = joinPoint.getArgs();
        //获取class对象
        Class targetClass = Class.forName(targetName);
        //获取对象中的所有方法
        Method[] methods = targetClass.getMethods();
        String des = "";
        //下面是进行匹配,方法名和目标方法名相同,且参数相同,则获取注解内的相应参数,用来进行日志记录
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    des = method.getAnnotation(ConfigAopLogService.class).description();
                    break;
                }
            }
        }
        return des;
    }

    /**
     * 获取操作表的操作类型
     * @param joinPoint
     * @return
     */
    private String getOperateType(JoinPoint joinPoint) throws ClassNotFoundException {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String operateType = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                if (method.getParameterTypes().length == args.length) {
                    operateType = method.getAnnotation(ConfigAopLogService.class).operateType();
                    break;
                }
            }
        }
        return operateType;
    }


    /**
     * 获取操作表的参数(你的参数是对象,会查出属性名及其值)
     * @param joinPoint
     * @return
     */
    private String getArgs(JoinPoint joinPoint) throws ClassNotFoundException {
        int index = -1; //记录下标
        String arg = "";//拼接参数
        Object[] args = joinPoint.getArgs();
        Signature signature = joinPoint.getSignature();//此处joinPoint的实现类是
        MethodSignature methodSignature = (MethodSignature) signature;//获取参数名
        for(int i=0;i<methodSignature.getParameterNames().length;i++){
            if (methodSignature.getParameterNames()[i].equals("request")){//获取参数名字为request的参数
                index = i;
            }
        }

        for (int j = 0; j<args.length; j++) {//循环参数对象
            if (index != -1 && index == j){ //不拼接request参数
                break;
            }
            Class<?> aClass = args[j].getClass();
            Field[] fields = aClass.getDeclaredFields();// 根据Class对象获得属性 私有的也可以获得
            try {
                for (Field f : fields) {
                    f.setAccessible(true); // 设置些属性是可以访问的
                    Object val = f.get(args[j]); // 得到此属性的值
                    String name = f.getName(); // 得到此属性的名称
                    arg += name + ":" + val + ", ";
                }
            } catch (IllegalAccessException e) {
                System.out.println("报错");
            }
        }
        System.out.println(arg);
        return arg;
    }
}

三.实际操作和数据库的值

1.把注解放在某个方法上

在这里插入图片描述

2.然后请求该方法,你的数据库就会存入对应的日志

操作描述操作类型 没办法自动获取所以我们手打就可以,而参数当前用户ip可以自动获取到,所以我们之间存入我们的实体类里面,然后直接存入数据库。

在这里插入图片描述

四.以上还需要你自己建立你想要的数据库表和交互数据库的接口

觉得还不错,点个赞吧,栓Q!

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

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

相关文章

两位前阿里 P10 的成长经历的启发

目录 汤峥嵘的成长经历 关键节点一&#xff1a;到美国留学 关键节点二&#xff1a;美国工作十年 关键节点三&#xff1a;八年阿里时光 关键节点四&#xff1a;加入途牛和 VIPABC 毕玄的成长经历 关键节点一&#xff1a;小公司里脱颖而出 关键节点二&#xff1a;加入淘宝…

FineReport数据分析教程- 图表刷新接口

1. 概述 1.1 预期效果 点击按钮可以刷新普通报表或决策报表中的图表&#xff0c;以普通报表为例&#xff0c;效果如下图所示&#xff1a; 1.2 实现思路 通过FR.Chart.WebUtils.getChart("chartID").dataRefresh()获取要刷新的图表对象&#xff0c;其中chartID为图表…

程序员如何写一份更好的简历

简历中的常见错误 1. 信息过多&#xff0c;缺乏重点 信息过多的常见表现是十几行的技能列表&#xff0c; 我举一个血淋淋的例子&#xff1a; 20 行的技能列表&#xff0c;这位求职者开始就把自己了解的所有工具都列出来&#xff0c;希望能够突显自己的经验和学习能力&#xf…

pytorch基础操作(五)多层感知机的实现

1、多层感知机 1、激活函数的引入 这个多层感知机有4个输⼊&#xff0c;3个输出&#xff0c;其隐藏层包含5个隐藏单元。输⼊层不涉及任何计算&#xff0c;因此使⽤此⽹络产⽣输出只需要实现隐藏层和输出层的计算。因此&#xff0c;这个多层感知机中的层数为2。注意&#xff0…

小米盒子为什么搜不到电视家?电视安装包解析错误解决方案

不少的朋友在小米电视盒子上安装了美家市场软件商店后&#xff0c;却发现在市场里面没法安装想要的电视盒子直播软件&#xff0c;这是怎么回事呢&#xff1f;其实大部分原因是电视盒子机制的问题限制了安装&#xff0c;导致部分品牌电视盒子装软件时会弹出“无法安装”的提示。…

Mysql双主整理

目录 1. Mysql binlog参数配置 2. Mysql binlog查看详细内容 3. Mysql双主搭建 4. Mysql双主解决数据回环 4.1 双主同步测试一 4.1.1 测试总结 4.2 双主同步测试二 4.2.1 测试总结 4.3 双主同步测试三 4.3.1 测试总结 1. Mysql binlog参数配置 log-binmysql-bin 打…

水果FLStudio21.0.0软件最新版有哪些新增功能变化?

FL Studio(水果软件)21 引入更快、更精确的音频编辑、改进的内容发现、对 DAW 情绪的控制以及更多鼓舞人心的创意工具。FL Studio是一款功能非常强大的音乐创作编辑软件它就是FL Studio(水果软件)。使用FL Studio中文版可以轻松帮我们制作自己的音乐唱片&#xff0c;拥有强大且…

【ROS】HelloWord简单实现

C实现 1. 创建工作空间并初始化 创建工作目录demo01_ws&#xff0c;并在该文件夹下创建src文件夹 mkdir -p demo01_ws/src进入到该目录下 cd demo01_ws/初始化 catkin_make这时在demo01_ws目录下除了src文件夹外&#xff0c;多处了两个文件夹。 ![在这里插入图片描述](htt…

数字化门店转型| 水疗会所管理系统| 小程序搭建

水疗会所与沐足采耳、洗浴按摩等都属于休息享受型服务&#xff0c;是不少中年人的选择&#xff0c;一天的压力可以得到缓解&#xff0c;同时客单价一般在几百元左右&#xff0c;在水疗会所里可以洗澡蒸桑拿、桌游、乒乓球等&#xff0c;同时随着近些年来生活压力逐渐加大&#…

【C语言学习】详解二级指针

在学习数据结构时&#xff0c;通常会遇到调用函数无法对主函数中的全局变量进行有效的更改操作&#xff0c;这时我们就需要指针&#xff0c;但二级指针对于初学者而言有着一定的学习难度&#xff0c;本文通过代码结合实验调试来详细说明二级指针。 如果一个指针指向另一个指针&…

[附源码]Node.js计算机毕业设计高校图书服务系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

Atcoder 前缀和优化DP Candies

Candies - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a; 思路&#xff1a; 考虑DP 状态设计&#xff1a; 首先&#xff0c;因为是线性DP&#xff0c;dp[i]是必不可少的 然后去考虑一下决策&#xff0c;看看是什么东西影响了决策 对于第 i 个位置&#xf…

stm32f767自举

仅作笔记 一&#xff0c;自举。 在M0&#xff0c;M3&#xff0c;M4内核中&#xff0c;是通过boot0和boot1两个引脚的电平组合来确定启动地址的&#xff0c;启动的介质可以是系统存储器&#xff0c;SRAM&#xff0c;主Flash等。 在M7内核中&#xff0c;是通过boot0的电平加 Fla…

Linux内核调试技术之动态调试

前言 使用printk的打印方式只能通过设置输出等级来进行控制&#xff0c;具备一定的局限性。在实际系统运行过程中&#xff0c;我们更希望能选择性地打开某些子系统或者模块的输出&#xff0c;为此内核提供了动态调试技术。内核中包括pr_debug、dev_dbg接口都使用了动态调试技术…

javax.crypto.BadPaddingException: Decryption error

问题描述 使用Postman调用Java api解密token时 token值为iRdLmVEYUUvoH1oDF2QhSVhJxXYMRCxzbtJsL01Iun2OLHY/FxNQOrAwF4Bj2cdp1vhsXt9BQtcxmiyuCvyi2Itl2qlvlCT6VwRM6UgQ5SBIiInGlLYCrzDfOoQ74zhxwW7M43vIuLs6W0y7Rt86uZgmAR8gYwMLfvGnRg 执行时报错如下&#xff1a; 原因分析…

Redis框架(十二):大众点评项目 阻塞队列+异步处理 实现秒杀优化

大众点评项目 阻塞队列异步处理 实现秒杀优化需求&#xff1a;阻塞队列异步处理 实现秒杀优化为什么使用异步处理&#xff1f;为什么使用阻塞队列&#xff1f;为什么使用Lua&#xff1f;业务逻辑及其实现原有逻辑代码 / 优化后逻辑代码完整优化业务代码原有优化业务代码总结Spr…

Traefik整理

entryPoints配置 defaultEntryPoints ["oneway"][entryPoints]# 代表traefik的监听端口为90[entryPoints.oneway]address ":90"#90端口接收到的请求先进行鉴权&#xff0c;traefik会先访问跳转到http://127.0.0.1:51001/t/test1进行权限验证[entryPoint…

如何使用加密sqlite数据库

如何使用加密sqlite数据库&#xff0c;起始有开源的sqlcipher 可以去开源网站搜索一下&#xff0c;如码云&#xff0c;github等&#xff0c;那么如何编译呢&#xff0c; 这是我的虚拟机版本和 config参数 然后就会生成Makefile 直接make即可生成 sqlcipher可执行程序&#xff…

有关Monaco的使用疑惑

前言 学习monaco editor已经有三个多月了。阅读了大部分的的文档&#xff0c;也看了很多相关文章&#xff0c;也调研了一些使用它做的大型开源项目。 一开始的疑惑已经逐渐解开&#xff0c;但随着学习的深入&#xff0c;也遇到了比较奇怪的问题。这篇文件就来聊一聊&#xff0…

软考考试多少分通过?

当然是45&#xff01;45&#xff01;45&#xff01;而且是各科45&#xff01; 初级和中级考两科 综合知识&#xff1a;考试时间为 150 分钟&#xff0c;笔试&#xff0c;选择题&#xff08;上午 9:00-11:30&#xff09;案例分析&#xff1a;考试时间为 90 分钟&#xff0c;笔…