AOP切面加自定义注解,实现日志记录

AOP切面加自定义注解,实现日志记录

  • 一、AOP
  • 二、准备工作
  • 三、添加AOP,把日志保存到数据库


一、AOP

  1. 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
  3. 我理解就是对接口执行前后耦合的地方添加统一的抽象方法
  4. 具体概念:Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
    Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
    Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
    Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
    Target(目标对象):织入 Advice 的目标对象.。
    Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

二、准备工作

  1. 使用spring3的后端项目为基础,开发工具idea 2023.3.2,jdk17。
  2. 在pom.xml中添加依赖在这里插入图片描述
<!-- AOP -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  1. 在mysql8.0.36中创建一个bus_log表,内容如下:在这里插入图片描述
  2. spring项目里正常添加实体类,service和mapper.
    添加文件Log如下:
package com.sumo.ipd.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "bus_log")
public class Log {
    /**
     * Log_ID
     */
    @TableId(value = "id",type = IdType.AUTO)
    private Long id;

    /**
     *操作人
     */
    @TableField(value = "name")
    private String name;

    /**
     *操作人id
     */
    @TableField(value = "certificate_no")
    private String certificateNo;

    /**
     * 操作记录
     */
    @TableField(value = "record")
    private String record;

    /**
     * 操作时间
     */
    @TableField(value = "time")
    private String time;

    /**
     * 操作ip
     */
    @TableField(value = "ip")
    private String ip;


    public static final String COL_ID="id";

    public static final String COL_NAME="name";

    public static final String COL_CERTIFICATENO="certificate_no";

    public static final String COL_RECORD="record";

    public static final String COL_TIME="time";

    public static final String COL_IP="ip";
}



添加文件LogMapper如下:

package com.sumo.ipd.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sumo.ipd.entity.Log;

public interface LogMapper extends BaseMapper<Log> {
}

添加文件LogService如下:

package com.sumo.ipd.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.sumo.ipd.entity.Log;

public interface LogService extends IService<Log> {
}

添加文件LogServiceImpl如下:

package com.sumo.ipd.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sumo.ipd.entity.Log;
import com.sumo.ipd.mapper.LogMapper;
import com.sumo.ipd.service.LogService;
import org.springframework.stereotype.Service;

@Service
public class LogServiceImpl extends ServiceImpl<LogMapper, Log> implements LogService {
}

三、添加AOP,把日志保存到数据库

创建两个文件BusLog和BusLogAop如下:在这里插入图片描述

BusLog内容如下:

package com.sumo.ipd.annotation;
import java.lang.annotation.*;

/**
 * <p> @Title SystemLog
 * <p> @Description 接口日志注解
 *
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BusLog {
    String name() default "" ;//操作人
    String record()  default "";//操作记录
}

BusLogAop内容如下:

package com.sumo.ipd.aop;

import com.sumo.ipd.entity.User;
import com.sumo.ipd.entity.Log;
import com.sumo.ipd.service.LogService;
import com.sumo.ipd.annotation.BusLog;
import jakarta.annotation.Resource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import java.lang.reflect.Method;


/**
 * 切面处理类,记录操作日志到数据库
 */
@Aspect
@Component
public class BusLogAop {

    @Resource
    LogService logService;

    //为了记录方法的执行时间
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 设置操作日志切入点,这里介绍两种方式:
     * 1、基于注解切入(也就是打了自定义注解的方法才会切入)
     *    @Pointcut("@annotation(com.woniu.pc.anno.MyLog)")
     * 2、基于包扫描切入
     *    @Pointcut("execution(public * org.wujiangbo.controller..*.*(..))")
     */
    @Pointcut("@annotation(com.sumo.ipd.annotation.BusLog)")//在注解的位置切入代码
    //@Pointcut("execution(public * com.woniu.pc.controller..*.*(..))")//从controller切入
    public void operLogPoinCut() {
    }

    @Before("operLogPoinCut()")
    public void beforMethod(JoinPoint point){
        startTime.set(System.currentTimeMillis());
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
//    @Pointcut("execution(* com.sumo.ipd.controller..*.*(..))")
//    public void operExceptionLogPoinCut() {
//    }

    /**
     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
     *
     * @param joinPoint 切入点
     * @param result      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "result")
    public void saveOperLog(JoinPoint joinPoint, Object result) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        HttpSession session = request.getSession();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            BusLog myLog = method.getAnnotation(BusLog.class);

            Log pcOperateLog = new Log();
            if (myLog != null) {
                //记录日志的操作人、内容
                pcOperateLog.setName(myLog.name());
                pcOperateLog.setRecord(myLog.record());
            }

            //获取操作者
            User currentUser = (User) session.getAttribute("user");
            if (currentUser != null) {
                pcOperateLog.setCertificateNo(currentUser.getCertificateNo());
            }

            //获取ip地址
            String ip = request.getRemoteHost();
            pcOperateLog.setIp(ip);

            long time =System.currentTimeMillis();
            pcOperateLog.setTime(String.valueOf(time));


            //调用Service方法,插入数据库
            logService.saveOrUpdate(pcOperateLog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用时只需要在controller的接口前添加注解@BusLog即可:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

debug调试高级功能 断点、布局 及Android Studio常用快捷按键使用详情

文章目录 debug断点篇&#xff1a;打临时断点&#xff08;只用一次&#xff09;&#xff1a;alt断点条件断点&#xff1a;在断点上&#xff0c;点击右键&#xff0c;在Condition那里&#xff0c;设置我们需要的值&#xff0c;循环就会自动停到我们设置的那个值那里依赖断点&…

Markdown如何分页操作

Markdown导出分页操作 在平时的文档导出过程中Markdown过程中会出现因为不能分页导致的排版问题。 排版问题在将Markdown文档导出为PDF或其他格式时尤为明显。当文档内容超过一页时&#xff0c;无法自动调整页面布局&#xff0c;导致内容不连续&#xff0c;甚至导致图片或表格…

pve8群晖rr方式安装(编译失败检查网络或磁盘空间error 23:200问题解决)

PVE 篇二&#xff1a;2024年PVE8最新安装使用指南|安装黑群晖&#xff5c;img格式镜像安装_NAS存储_什么值得买 (smzdm.com) 黑群晖 篇五&#xff1a;2023黑群晖最新安装方式|RR新手也可轻松上手_NAS存储_什么值得买 (smzdm.com) 编译引导提示&#xff1a;检查网络或磁盘空间er…

qt dll编写和调用

dll编写 新建项目 头文件 #ifndef LIB1_H #define LIB1_H#include "lib1_global.h"class LIB1_EXPORT Lib1 { public:Lib1(); };//要导出的函数&#xff0c;使用extern "C"&#xff0c;否则名称改变将找不到函数extern "C" LIB1_EXPORT int ad…

程序员的核心职业素养:专业、沟通与持续学习

✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的点赞、关注、收藏、评论&#xff0c;是对我最大…

单片机第五季-第八课:STM32CubeMx和FreeRTOS

1&#xff0c;FreeRTOS背景介绍 RTOS简介&#xff1a; 实时操作系统&#xff0c;本用于追求实时性的嵌入式系统&#xff0c;典型&#xff1a;ucos/uclinux/vxworks&#xff1b; 特点&#xff1a;中断响应快、一般可嵌套中断、使用实地址、多任务&#xff1b; &#xff08;实…

中国历年人均发电量统计报告

数据来源于国家统计局&#xff0c;为1978年到2020年我国每年的人均发电量数据。 2020年&#xff0c;我国人均发电量为5512.76千瓦时&#xff0c;比上年增长3.4%。 数据统计单位为&#xff1a;千瓦时 我国人均发电量有多少&#xff1f; 2020年&#xff0c;我国人均发电量为5512…

一键自动粘贴,高效处理邮箱地址,让你的工作效率翻倍提升!

在信息爆炸的时代&#xff0c;邮箱地址已成为我们日常工作和生活中的必备元素。无论是商务沟通、报名注册还是信息传递&#xff0c;邮箱地址都扮演着至关重要的角色。然而&#xff0c;手动复制粘贴邮箱地址的繁琐操作往往让人头疼不已&#xff0c;不仅效率低下&#xff0c;还容…

代码随想录第29天|贪心算法part3

134.加油站 首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈 每个加油站的剩余量rest[i]为gas[i] - cost[i] 从0开始累加rest[i]&#xff0c;和记为curSum&#xff0c;一旦curSum小于零&#xff0c;说明[0, i]区间都不能作为起始位置 因为我们一直维护的是一个剩余量大…

Linux磁盘格式化与重新分区

1.df -BG查看磁盘挂载情况 2.fdisk -l查看磁盘详细信息 3.sudo mkfs.ext4 /path 格式化磁盘 4.挂载格式化后磁盘 挂载成功

FreeRTOS简单内核实现5 阻塞延时

文章目录 0、思考与回答0.1、思考一0.2、思考二0.3、思考三 1、创建空闲任务2、实现阻塞延时3、修改任务调度策略4、提供延时时基4.1、SysTick4.2、xPortSysTickHandler( )4.3、xTaskIncrementTick( ) 5、实验5.1、测试5.2、待改进 0、思考与回答 0.1、思考一 为什么 FreeRTO…

C++ 47 之 函数调用运算符重载

#include <iostream> #include <string> using namespace std;class MyPrint{ public:// 重载小括号() 重载谁operator后就紧跟谁的符号void operator()(string txt){cout << txt << endl;} };class MyAdd{ public:int operator()(int a, int b){retur…

springboot汽车配件管理系统(源码+sql+论文报告)

绪论 1.1 研究意义和背景 随着我国经济的持续发展&#xff0c;汽车已经逐步进入了家庭。汽车行业的发展&#xff0c;也带动了汽车配件行业的快速发展。 汽车配件行业的迅猛发展&#xff0c; 使得汽配行业的竞争越来越激烈。如何在激烈的竞争中取胜&#xff0c;是每家汽车零部…

Java实现异步开发的方式

1&#xff09;、继承 Thread 2&#xff09;、实现 Runnable 接口 3&#xff09;、实现 Callable 接口 FutureTask &#xff08;可以拿到返回结果&#xff0c;可以处理异常&#xff09; 4&#xff09;、使用线程池 区别&#xff1a;1、2&#xff09;不能得到返回值 …

人工智能对零售业的影响

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、人工智能如何改变零售格局二、利用人工智能实现购物体验自动化三、利用人工智能改善库存管理四、通过人工智能解决方案增强客户服务五、利用人工智能分析消费者行为六、利用 AI 打造个性化…

C++封装TCP类,包括客户端和服务器

头文件 XTcp.h #ifndef XTCP_H #define XTCP_H#ifdef WIN32 #ifdef XSOCKET_EXPORTS #define XSOCKET_API __declspec(dllexport) #else #define XSOCKET_API __declspec(dllimport) #endif #else #define XSOCKET_API #endif#include <string> XSOCKET_API std::string…

【git使用四】git分支理解与操作(详解)

目录 &#xff08;1&#xff09;理解git分支 主分支&#xff08;主线&#xff09; 功能分支 主线和分支关系 将分支合并到主分支 快速合并 非快速合并 git代码管理流程 &#xff08;2&#xff09;理解git提交对象 提交对象与commitID Git如何保存数据 示例讲解 &a…

Bio-Info每日一题:Rosalind-07-Mendel‘s First Law(孟德尔第一定律 python实现)

&#x1f389; 进入生物信息学的世界&#xff0c;与Rosalind一起探索吧&#xff01;&#x1f9ec; Rosalind是一个在线平台&#xff0c;专为学习和实践生物信息学而设计。该平台提供了一系列循序渐进的编程挑战&#xff0c;帮助用户从基础到高级掌握生物信息学知识。无论你是初…

C++前期概念(重)

目录 命名空间 命名空间定义 1. 正常的命名空间定义 2. 命名空间可以嵌套 3.头文件中的合并 命名空间使用 命名空间的使用有三种方式&#xff1a; 1:加命名空间名称及作用域限定符&#xff08;::&#xff09; 2:用using将命名空间中某个成员引入 3:使用using namespa…

Milvus Cloud 问答机器人 上线!构建企业级的 Chatbot

01. 背景 早些时候我们在社区微信群发出了一份关于Milvus Cloud 自动问答机器人的调研问卷。 调研受到了社区同学的积极响应,很快我们就收到了很多热心用户的回复。 基于这些回复,我们整理出了 Milvus Cloud Chatbot 的形态: 以功能使用和文档查询为核心 提供聊天和搜索双形…