springboot项目自定义切面增强方法功能(springboot记录日志)

说明

背景:记录系统接口日志入库,包含接口方法、入参、回参、响应时间、操作人、操作时间等信息。

方案:添加自定义切面处理

一、自定义切面注解

java 复制代码
package com.gstanzer.supervise.annotation;

import com.gstanzer.supervise.enums.BusinessType;

import java.lang.annotation.*;

/**
 * 系统操作日志
 *
 * @author: tangbingbing
 * @date: 2023/12/15 15:25
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SysOperLog {
    /**
     * 接口名称
     */
    String name();

    /**
     * 接口功能
     */
    BusinessType businessType() default BusinessType.OTHER;

}

二、自定义切面

java 复制代码
package com.gstanzer.supervise.aspect;


import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.gstanzer.supervise.annotation.SysOperLog;
import com.gstanzer.supervise.mapper.SysOperLogMapper;
import com.gstanzer.supervise.po.SysOperLogPO;
import com.gstanzer.supervise.utils.IpUtils;
import com.gstanzer.supervise.utils.IpUtilsNew;
import com.gstanzer.supervise.utils.ServletUtils;
import com.gstanzer.supervise.utils.TokenUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
 * 系统操作日志记录处理
 *
 * @author: tangbingbing
 * @date: 2023/12/15 15:25
 */
@Aspect
@Component
public class SysOperLogAspect {
    private static final Logger log = LoggerFactory.getLogger(SysOperLogAspect.class);

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Resource
    private SysOperLogMapper sysOperLogMapper;

    @Before("@annotation(controllerLog)")
    public void doBefore(JoinPoint joinPoint, SysOperLog controllerLog) {
        //开始计时
        startTime.set(System.currentTimeMillis());
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, SysOperLog controllerLog, Object jsonResult) {
        HttpServletRequest request = IpUtils.getHttpServletRequest();
        handleLog(joinPoint, controllerLog, null, jsonResult, request);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, SysOperLog controllerLog, Exception e) {
        HttpServletRequest request = IpUtils.getHttpServletRequest();
        handleLog(joinPoint, controllerLog, e, null, request);
    }

    protected void handleLog(final JoinPoint joinPoint, SysOperLog controllerLog, final Exception e, Object jsonResult, HttpServletRequest request) {
        try {
            long totalRuntime = System.currentTimeMillis() - startTime.get();
            String userId = "";
            String loginName = "";
            Date date = new Date();
            String token = request.getHeader("gst-token");
            Map<String, String> userMap = TokenUtils.getUserInfoByToken(token);
            if (userMap != null) {
                userId = userMap.get("userId");
                loginName = userMap.get("loginName");
            }
            SysOperLogPO operLog = new SysOperLogPO();
            String ip = IpUtilsNew.getIpAddr(ServletUtils.getRequest());
            operLog.setId(String.valueOf(IdUtil.getSnowflake(1,1).nextId()));
            operLog.setOperIp(ip);
            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
            operLog.setUserId(userId);
            operLog.setUserName(loginName);
            if (e != null) {
                operLog.setErrorMsg(e.getMessage());
            }
            //String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethodName(methodName);
            operLog.setOperTime(date);
            operLog.setResponseTime(totalRuntime + "ms");
            // 设置请求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
            // 保存数据库
            log.info("操作日志入库:{}", operLog);
            sysOperLogMapper.insert(operLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log     日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, SysOperLog log, SysOperLogPO operLog, Object jsonResult) throws Exception {
        operLog.setMethodName(log.name());
        operLog.setBusinessType(String.valueOf(log.businessType()));
        setRequestValue(joinPoint, operLog);
        operLog.setResponseParam(JSON.toJSONString(jsonResult));
    }

    /**
     * 获取请求的参数,放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLogPO operLog) throws Exception {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setRequestParam(params);
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (o != null && !isFilterObject(o)) {
                    try {
                        String jsonObj = JSON.toJSONString(o);
                        params += jsonObj + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}

三、在需要记录日志的接口上加上该注解即可

相关推荐
sjmaysee8 分钟前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
渔阳节度使1 小时前
SpringAI实时监控+观测性
后端·python·flask
Victor3561 小时前
MongoDB(42)如何使用$project阶段?
后端
Victor3561 小时前
MongoDB(43)什么是嵌入式文档?
后端
1.14(java)1 小时前
Spring-boot快速上手
java·开发语言·javaee
Darkdreams2 小时前
SpringBoot项目集成ONLYOFFICE
java·spring boot·后端
bropro2 小时前
【Spring Boot】Spring AOP中的环绕通知
spring boot·后端·spring
lhbian2 小时前
【Spring Cloud Alibaba】基于Spring Boot 3.x 搭建教程
java·spring boot·后端
luom01022 小时前
springcloud springboot nacos版本对应
spring boot·spring·spring cloud
IT_陈寒2 小时前
Redis性能提升3倍的5个冷门技巧,90%开发者都不知道!
前端·人工智能·后端