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

目录

一.Maven依赖

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

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

四.创建切面类

五.测试

5.1controller类

5.2 postman测试数据

步骤:

  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测试数据

人员表

操作日志记录表:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值