SpringBoot 自定义注解实现操作日志

本文介绍了一种利用Spring AOP和自定义注解实现操作日志记录的方法。通过在Controller层接口上添加注解并指定事件名称和主键,可以在数据库中记录操作人、事件详情及时间等信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前提

前段时间客户提出一个需求,想知道谁进了系统干了什么事,但又不是什么都需要记录。所以就想写个注解与切面来实现这个功能。代码粗糙,有什么不对的地方望大神指正。

实现效果

注解放于controller的接口,填上事件名。因为我想记录操作对象的主键是什么,所以还有个id,这个id填的是我传递对象的主键名称。具体做法看下面代码。
注解使用

在用户界面访问接口后,数据库中将记录操作人,事件名。传递的参数是什么。以及创建时间。有这个数据在,前端就可以按需进行展示了。
数据库

代码

  1. 注解类
/**
* Description: 简单记录操作日志
* @return   返回类型
* @author 格兰德法则·祝
* Create time 2021/5/24 10:26
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperationLog {
    // 可以直接填入人员。这个项目中人员是通过前端传递的token进行解析获取的人员
    String name() default "未知人员";
    // 事件名称,根据接口用途自行填入。
    String event() default "未知事件";
    // 对象主键名称,要根据传递对象的主键来进行修改。若是传的是个String。那么直接从参数(数据库)中获取。
    String id() default "未知主键";
}

2.切面类

/**
* Description: 日志切面
* @return   返回类型
* @author 格兰德法则·祝
* Create time 2021/5/24 10:26
*/
@Component
@Aspect
@Slf4j
public class OperationLogAspect {
    @Resource
    private UserOrgManageFeign userOrgManageFeign;
    @Resource
    private LogInfoMapper logInfoMapper;

    @Pointcut("@annotation(com.dlg.patrol.annotation.OperationLog)")
    private void pointCut(){}

    @Before("pointCut()&& @annotation(operationLog)")
    public void before(OperationLog operationLog){
    }

    @Around("pointCut() && @annotation(operationLog)")
    public Object advice(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws JsonProcessingException {
        String realName = "";
        String id = "";
        String value = "";
        Object result = null;
//        获取接口参数
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (args[i] == null) {
                continue;
            }
            // 这是注解中的ID,根据注解上填写的id="id名",通过 id名 反射获取传递的对象类中相应ID
            if (!"未知主键".equals(operationLog.id()) && !(args[i] instanceof HttpServletRequest)) {
            // ↓反射获取主键。这里比较粗糙。
            // 主键一般在继承的父类中,并且是私有的。
                Field[] fields = args[i].getClass().getFields();
                Field[] declaredFields = args[i].getClass().getSuperclass().getDeclaredFields();
                try {
                    for (Field field : fields) {
                        field.setAccessible(true);
                        if (operationLog.id().equals(field.getName())) {
                            id = (String)field.get(args[i]);
                        }
                    }
                    for (Field field : declaredFields) {
                        field.setAccessible(true);
                        if (operationLog.id().equals(field.getName())) {
                            id = (String)field.get(args[i]);
                        }
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            // 这是获取操作人的。我们项目中是通过HttpServletRequest中的header中的token来获取人员信息的。这里可以根据自身项目情况进行修改
            if(args[i] instanceof HttpServletRequest) {
                HttpServletRequest request = (HttpServletRequest)args[i];
                String userId = RedisUtil.getUserId(request);
                realName = userOrgManageFeign.getUserInfo(userId).getData().getRealName();
                break;
            }else {
                ObjectMapper mapper = new ObjectMapper();
                value = value + " parameter:  " + mapper.writeValueAsString(args[i]);
            }
        }
   // 这里就是存进操作日志表的操作
        try {
            LogInfo logInfo = new LogInfo();
            logInfo.setCreatedOn(new Date());
            logInfo.setEvent(operationLog.event());
            logInfo.setLogId(UUID.randomUUID().toString());
            if(!"".equals(id)){
                logInfo.setEventId(id);
            }
            logInfo.setParameter(value);
            logInfo.setUserName(realName);
            logInfoMapper.insertSelective(logInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        try {
            result = joinPoint.proceed(args);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        // 这里是返回的信息了,不做处理直接返回,结合自身项目进行修改
        if(result instanceof ApiResult) {
            ApiResult apiResult = (ApiResult) result;
            return apiResult;
        }
        return result;
    }



    @After("pointCut()&& @annotation(operationLog)")
    public void after(OperationLog operationLog){
    }
}

3.使用
注解的使用上面有就不在描述了。

总结

水平有限,写的比较简单,有什么不对的地方希望大神指点。谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值