自定义注解+AOP实现操作日志记录方式

 更新时间:2026年01月14日 09:24:27   作者:小憨憨的牛  
文章介绍了如何使用AOP实现操作日志记录,包括创建操作日志表和实体类、自定义注解、切面类以及测试

步骤:

  1. 引入AOP依赖
  2. 创建操作日志表和实体类
  3. 创建自定义注解(用于记录哪些方法需要记录日志)
  4. 创建切面,监听自定义的注解

一.Maven依赖

Swagger也是用来记录日志的,可加可不加

        <!-- AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- Swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>

二.创建日志记录表和实体类

create_time和update_time字段在BaseEntity里面,且为自动填充

package com.dhp.ets.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.util.Date;

/**
 * @Description
 * @Author dhp
 * @create 2025-05-12 10:29
 */
@Data
@TableName(value = "sys_action_log")
public class SysActionLogBean extends BaseEntity{

    @TableId
    private String id;
    /*
        操作人
        */
    private String userId ;
    /*
    操作人姓名
    */
    private String userName ;
    /*
    操作类型
    */
    private String actionType ;
    /*
    操作对象
    */
    private String actionClass ;
    /*
    操作方法
    */
    private String actionMethod ;
    /*
    操作参数
    */
    private String actionParams ;
    /*
    操作时间
    */
    private Date actionTime ;
    /*
    操作IP
    */
    private String actionIp ;
}

三.创建记录日志自定义注解

该注解的value值用于记录操作类型是什么,比如新增,修改,删除.....

/**
 * 用于操作日志记录
 */

@Target(ElementType.METHOD) //该注解用于方法上
@Retention(RetentionPolicy.RUNTIME) //运行期间仍然有效,可以在运行时通过反射获取注解的属性和值
public @interface SysLog {
    /**
     * 是否允许为空
     * @return
     */
    String value() default "";

}

四.创建切面类

controller类用Swagger的@Api注解记录该控制层的所有方法是针对什么的,在方法上用@SysLog自定义注解用于记录该方法具体做什么的,该切面类用于监听用了@SysLog注解的方法,获取该方法和类的信息

该切面类没有记录人员信息,具体根据实际项目来写入

package com.dhp.utils.syslog;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dhp.ets.entity.SysActionLogBean;
import com.dhp.ets.service.SysActionLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.regex.Pattern;

/**
 * 系统日志:切面处理类
 */
@Aspect
@Component
public class SysLogAspect {

    private static Logger logger = LoggerFactory.getLogger(SysLogAspect.class);

    @Autowired
    private SysActionLogService sysActionLogService;

    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(com.dhp.utils.syslog.SysLog)")
    public void logPoinCut() {
        logger.info("记录操作日志");
    }

    //切面 配置通知
    //@AfterReturning("logPoinCut()")
    @Before("logPoinCut()")
    public void saveSysLog(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //保存日志
        SysActionLogBean sysActionLog = new SysActionLogBean();

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        //获取操作
        SysLog sysLog = method.getAnnotation(SysLog.class);
        if (sysLog != null) {
            String value = sysLog.value();
            sysActionLog.setActionType(value);//保存获取的操作
        }

        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        String  classChineseName = "";
        //获取类的注解
        Annotation[]  annotations = joinPoint.getTarget().getClass().getAnnotations();
        for(Annotation annotation :annotations){
            if(annotation.annotationType().getName().indexOf("io.swagger.annotations.Api")>=0){
                //提取中文字符串
                  classChineseName =  getAnnotationDesc(annotation.toString());
            }
        }
        //获取请求的方法名
        String methodName = method.getName();

        //处理请求的类名,只保留最后一段  并过滤掉操作日志本身
        String classSimpleName ;
        String[] classPaths = className.split("\\.");
        if(classPaths.length>0){
            classSimpleName = classPaths[classPaths.length-1];
        }else{
            classSimpleName = className;
        }

        //请求的参数
        Object[] args = joinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = JSON.toJSONString(args);

        //处理新增和修改参数,过滤掉不重要的字段
        if (methodName.equals("save")) {
            JSONArray jsonArray = JSONArray.parseArray(params);
            JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(0).toString());
            jsonObject.remove("createTime");
            jsonObject.remove("updateTime");
            if (jsonObject.containsKey("id")) {
                methodName = "update";
                sysActionLog.setActionType("修改");
            }
            params = jsonObject.toJSONString();
        }

        sysActionLog.setActionClass(classSimpleName);
        if(classChineseName!=null&&classChineseName.length()>0){
            sysActionLog.setActionClass(classChineseName);
        }

        sysActionLog.setActionMethod(methodName);
        sysActionLog.setActionParams(params);
        logger.info("操作参数:{}",params);
        sysActionLog.setActionTime(new Date());
        //获取用户名
//        if(ShiroUtils.getUser()!=null) {
//            sysActionLog.setUserId(ShiroUtils.getUser().getId());
//            sysActionLog.setUserName(ShiroUtils.getUser().getUserName());
//        }
        sysActionLog.setActionIp(getIpAddress(request));

        //调用service保存SysLog实体类到数据库
        sysActionLogService.saveOrUpdate(sysActionLog);
    }

    public final  String getIpAddress(HttpServletRequest request)  {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || "".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                //根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    logger.error("获取IP异常", e );
                }
                if(inet!=null){
                    ipAddress = inet.getHostAddress();
                }
            }
        }
        if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(',') >= 0) { //"***.***.***.***".length() = 15
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(','));
        }
        return ipAddress;
    }


//提取中文字符串
    public  String  getAnnotationDesc(String desc){
        String pattern = "[\\u4E00-\\u9FA5]+";
        StringBuilder  sb  = new StringBuilder();
        String[] splitStr = desc.split("");
        for(String str:splitStr) {
            if(Pattern.matches(pattern, str)){
                sb = sb.append(str) ;
            }
        }
        return sb.toString();
    }
}

五.测试

5.1controller类

@Api注解记录该类的方法是人员管理的方法,@SysLog用于记录保存操作

/**
 * @Author dhp
 * @create 2025-02-24 15:07
 */
@Api(description = "人员管理")
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @ApiOperation(value = "保存用户")
    @PostMapping("save")
    @SysLog("保存")
    public WebResult save(@RequestBody User user){
        return userService.saveUsers(user);
    }
}

5.2 postman测试数据

人员表

操作日志记录表:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • mybatis通过if语句实现增删改查操作

    mybatis通过if语句实现增删改查操作

    这篇文章主要介绍了mybatis通过if语句实现增删改查操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • 详细介绍idea如何设置类头注释和方法注释(图文)

    详细介绍idea如何设置类头注释和方法注释(图文)

    本篇文章主要介绍了idea如何设置类头注释和方法注释(图文),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • Kotlin lateinit与by lazy案例详解

    Kotlin lateinit与by lazy案例详解

    这篇文章主要介绍了Kotlin lateinit与by lazy案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • MyBatisCodeHelperPro最新激活方法(有效方法)

    MyBatisCodeHelperPro最新激活方法(有效方法)

    这篇文章主要介绍了MyBatisCodeHelperPro最新激活方法亲测有效,非常好用,小编今天以idea2021.2.1为例给大家详细讲解,需要的朋友可以参考下
    2022-08-08
  • 浅谈在页面中获取到ModelAndView绑定的值方法

    浅谈在页面中获取到ModelAndView绑定的值方法

    下面小编就为大家分享一篇浅谈在页面中获取到ModelAndView绑定的值方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • idea中java及java web项目的常见问题及解决

    idea中java及java web项目的常见问题及解决

    在IDEA中处理乱码问题主要涉及四个方面:文件编码设置为UTF-8、编辑器默认编码调整、Tomcat运行配置编码设置以及解决cmd中的乱码,此外,详细介绍了在IDEA中创建Web项目的步骤,包括新建Java工程、添加Web框架支持、添加Tomcat依赖库
    2024-09-09
  • Java多线程之原子类解析

    Java多线程之原子类解析

    这篇文章主要介绍了Java多线程之原子类解析,Java原子类是一种多线程编程中常用的工具,用于实现线程安全的操作,它们提供了一种原子性操作的机制,确保多个线程同时访问共享变量时的数据一致性,需要的朋友可以参考下
    2023-10-10
  • 构建多模块的Spring Boot项目步骤全纪录

    构建多模块的Spring Boot项目步骤全纪录

    这篇文章主要给大家介绍了关于如何构建多模块的Spring Boot项目的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-05-05
  • SpringBoot使用PageHelper插件实现Mybatis分页效果

    SpringBoot使用PageHelper插件实现Mybatis分页效果

    这篇文章主要介绍了SpringBoot使用PageHelper插件实现Mybatis分页效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作有一定的参考借鉴价值,需要的朋友可以参考下
    2024-02-02
  • Java如何识别图片或扫描PDF中的文字详解

    Java如何识别图片或扫描PDF中的文字详解

    这篇文章主要介绍了Java如何识别图片或扫描PDF中文字的相关资料,介绍了Java中使用Spire.OCRforJava库来识别图片和扫描PDF文件中的文字,需要的朋友可以参考下
    2025-01-01

最新评论