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

说明

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

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

一、自定义切面注解

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;

}

二、自定义切面

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;
    }
}

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

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
springboot学习笔记 spring基础 Spring概述 Spring的简史 xml配置 注解配置 java配置 Spring概述 Spring的模块 核心容器CoreContainer Spring-Core Spring-Beans Spring-Context Spring-Context-Support Spring-Expression AOP Spring-AOP Spring-Aspects Messaging Spring-Messaging WEB Spring-Web Spring-Webmvc Spring-WebSocket Spring-Webmvc-Portlet 数据访问/集成(DataAccess/Intefration) Spring-JDBC Spring-TX Spring-ORM Spring-OXM Spring-JMS Spring的生态 Spring Boot Spring XD Spring Cloud Spring Data Spring Integration Spring Batch Spring Security Spring HATEOAS Spring Social Spring AMQP Spring Mobile Spring for Android Spring Web Flow Spring Web Services Spring LDAP Spring Session Spring项目快速搭建 Maven简介 Maven安装 Maven的pom.xml dependencies dependency 变量定义 编译插件 Spring项目的搭建 Spring Tool Suite https://spring.io/tools/sts/all IntelliJ IDEA NetBeans https://netbeans.org/downloads/ Spring基础配置 依赖注入 声明Bean的注解 @Component组件,没有明确的角色 @Service在业务逻辑层(service层) @Repository在数据访问层(dao层) @Controller在展现层(MVC→SpringMVC) 注入Bean的注解 @Autowired:Spring提供的注解 @Inject:JSR-330提供的注解 @Resource:JSR-250提供的注解 Java配置 @Configuration声明当前类是一个配置类 @Bean注解在方法上,声明当前方法的返回值为一个Bean AOP @Aspect 声明是一个切面 拦截规则@After @Before @Around PointCut JoinPoint Spring常用配置 Bean的Scope Singleton Prototype Request Session GlobalSession SpringEL和资源调用 注入普通字符 注入操作系统属性 注入表达式云算结果 注入其他Bean的属性 注入文件内容 注入网址内容 注入属性文件 Bean的初始化和销毁 Java配置方式 注解方式 Profile @Profile 通过设定jvm的spring.profiles.active参数 web项目设置在Servlet的context parameter中 事件Application Event 自定义事件,集成ApplicationEvent 定义事件监听器,实现ApplicationListener 使用容器发布事件 Spring高级话题 Spring Aware BeanNameAware BeanFactoryAware
基于Springboot+Vue的房价可视化监测系统源码+项目说明.zip 【资源说明】 1、该资源内项目代码都是经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能。 一个基于Springboot+Vue的房价可视化监测系统,数据抓取自链家,通过Echarts进行可视化展现。 ### 1.1 项目介绍 [1] 爬取链家的新楼盘、二手房、小区、租房信息作为分析数据,支持北上广深等国内21个主要城市。 [2] 通过 IP 代理池、伪装 User-Agent 、随机延时、多线程等手段快速稳定抓取数据。 [3] 通过 xxl-job 定时任务实现爬虫的自动抓取及数据的自动汇总处理。 [4] 有完善的用户、角色、权限配置及管理机制,支持页面级、按钮级的权限管理。 [5] 通过 AOP 切面自定义注解实现系统日志记录,可在系统管理页面查看操作日志。 [6] 集成 Druid 数据连接池、Screw 导出数据库结构、Swagger 生成接口文档。 [7] 通过 axios 请求后端接口,使用 VueX 缓存用户登录状态及系统字典。 [8] 使用 Element-UI 组件库开发页面,Echarts 开发可视化图表,二者均简单易用。 ### 1.2 项目技术栈 后端部分:[Springboot](https://github.com/spring-projects/spring-boot)(后端核心框架)、[Mybatis](https://github.com/mybatis/mybatis-3)(持久化框架)、[Shiro](https://github.com/apache/shiro)(权限框架)、[Druid](https://github.com/alibaba/druid)(数据库连接池)、[Screw](https://github.com/pingfangushi/screw)(数据库表结构生成器)、[Swagger](https://github.com/swagger-api/swagger-ui)(接口文档)、[Hutool](https://github.com/looly/hutool)(Java工具类库)、[Lombok](https://www.projectlombok.org/)(简化代码)、[MySQL](https://www.mysql.com/cn/)(数据库)、[Redis](https://redis.io/)(高速缓存) 前端部分:[Vue](https://cn.vuejs.org/v2/guide/index.html)(前端核心框架)、[VueX](https://vuex.vuejs.org/zh/)(状态管理)、[axios](https://github.com/axios/axios)(HTTP请求库)、[Element-UI](https://element.eleme.cn/#/zh-CN/component/installation)(基础组件库)、[Echarts](https://echarts.baidu.com/theme-builder/)(可视化图表) 爬虫部分:[Xpath](https://www.w3.org/TR/xpath/)(Python爬虫核心模块)、[ProxyPool](https://github.com/jhao104/proxy_pool)(Python爬虫IP代理池) 定时任务:[xxl-job](https://github.com/xuxueli/xxl-job/)(分布式任务调度平台,分为调度中心 xxl-job-admin 和执行器 xxl-job-executor 两部分) 开发工具:IDEA、PyCharm、VScode、Git、Maven、Navicat ### 1.3 项目结构 项目主要结构如下: ``` house-price-monitor ├── admin │ ├── pom.xml │ ├── src/main/java/com/house │ ├── ├── AdminApplication.java │ ├── ├── aop │ ├── ├── config │ ├── ├── constant │ ├── ├── controller

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr Tang

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值