SpringCloud/SpringBoot多模块项目中配置公共AOP模块实现打印子模块Controller所有请求参数与日志

news2025/1/22 9:18:38

项目中遇到多个模块需要打印Controller请求日志,在每个模块里面加AOP并且配置单独的切面笔者认为代码冗余,于是乎就打算把AOP日志打印抽离成一个公共模块,谁想用就引入Maven坐标就行。

定义公共AOP模块 并编写AOP工具

AOP模块pom.xml如下


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
   这里根据自己需要引入 公共AOP父模块
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xx-common-aop</artifactId>

    <description>
        xx-common-aop切面
    </description>

    <dependencies>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- SLF4J -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!-- FastJSON -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>
        <!-- Hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

AOP核心代码





import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
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.util.*;

/**
 * 类名称: ServiceLogAop 
 * 类描述: api入参, 出参打印
 */
@Aspect
@Component
@Slf4j
public class ServiceLogAop {


    /**
     * 换行符
     */
    private static final String LINE_SEPARATOR = System.lineSeparator();

    /**
     * 以自定义 @ServiceLogAop 注解为切点
     */
    @Pointcut("execution(public *  com.*.*.controller.*.*(..))")
    public void webLog() {
    }


    //基本类型定义,用于判断请求参数的类型
    private static List typeList = new ArrayList();
    private static String[] types = {"java.lang.Integer", "java.lang.Double",
            "java.lang.Float", "java.lang.Long", "java.lang.Short",
            "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
            "java.lang.String", "int", "double", "long", "short", "byte",
            "boolean", "char", "float"};

    static {
        for (int i = 0; i < types.length; i++) {
            typeList.add(types[i]);
        }
    }


    /**
     * 在切点之前织入
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 获取 @WebLog 注解的描述信息

        // 打印请求相关参数
        log.info("========================================== Start ==========================================");
        // 打印请求 url
        log.info("URL           : {}", request.getRequestURL().toString());
        //uri
        String uri = request.getRequestURI();
        log.info("URI           : {}", uri);
        // 打印 Http method
        log.info("HTTP Method   : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        log.info("Class Method  : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        //IP
        log.info("IP             : {}", request.getRemoteAddr());
        // 打印请求的 IP
        log.info("IP             : {}", ServletUtil.getClientIP(request));
        //Token
        String authorization = request.getHeader("Authorization");
        log.info("Authorization  : {}", authorization);
/*        //cokie
        String Cookie = request.getHeader("Cookie");
        log.info("Cookie         : {}", Cookie);*/
        //User-Agent
        String userAgent = request.getHeader("User-Agent");
        log.info("User-Agent     : {}", userAgent);


        StringBuffer requestParam = new StringBuffer("");
        //参数
        Enumeration<String> paramter = request.getParameterNames();
        while (paramter.hasMoreElements()) {
            String str = (String) paramter.nextElement();
            String strValue = request.getParameter(str);
            requestParam.append(str + "=" + strValue + "&");
        }
        log.info("Form请求参数     : {}", requestParam.toString());
        // 打印请求入参
        //   log.info("Json请求参数      : {}", JSONUtil.toJsonStr(joinPoint.getArgs()));


        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 通过这获取到方法的所有参数名称的字符串数组
        String[] parameterNames = methodSignature.getParameterNames();//获取到所有参数的NAME
        Object[] args = joinPoint.getArgs(); //获取到所有参数的VALUE
        StringBuilder sb = new StringBuilder();
        Map paramMap = new HashMap();
        if (parameterNames != null && parameterNames.length > 0 && args != null && args.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                //考虑入参要么为基础类型参数,要么为对象类型。以下方法都适合解析出来
                if (typeList.contains(args[i].getClass().getTypeName())) {
                    //基本数据类型
                    paramMap.put(parameterNames[i], JSON.toJSONString(args[i]));
                } else {
                    //实体类
                    paramMap = JSON.parseObject(JSON.toJSONString(args[i]));
                }
            }
        }

        log.info("FormAndJsonPram: " + paramMap.toString());


    }

    /**
     * 在切点之后织入
     *
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() {
        // 接口结束后换行,方便分割查看
        log.info("=========================================== End ===========================================" + LINE_SEPARATOR);
    }

    /**
     * 环绕
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
        //  log.info("Response Args  : {}", JSONUtil.toJsonStr(result));
        // 执行耗时
        log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        return result;
    }


}


@Pointcut(“execution(public * com.ruoyi..controller..*(…))”) 作为AOP切入口
此切入口必须为静态常量 不能为动态参数 所以说从yml读取切入点是干不成。
但 @Pointcut 可以同时配置多个参数 这个就可以解决多个不同路径下面的Controller都能被切入进来。

@Pointcut("execution(* xx.xx.xxx.xx.*.save*(..))"
			+ "||execution(* xx.xx.xx.*.delete*(..))"
			+ "||execution(* xx.xx.xx..*.update*(..))")  

子模块

  1. 使用需要引入公共AOP模块maven

  2. 创建图片中目录文件 并引入公共模块定义好的ServiceLogAop ( 这里也可以通过 @Bean方式把ServiceLogAop 注册进来 )
    在这里插入图片描述

  3. 效果
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/925087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot案例-配置文件-yml配置文件

配置格式 SpringBoot提供了多种属性配置方式 application.propertiesapplication.ymlapplication.yaml常见配置文件格式对比 XML&#xff08;臃肿&#xff09; <configuration><database><host>localhost</host><port>3306</port><use…

VMware虚拟机连不上网络

固定ip地址 进入网络配置文件 cd /etc/sysconfig/network-scripts 打开文件 vi ifcfg-ens33 编辑 BOOTPROTO设置为static&#xff0c;有3个值&#xff08;decp、none、static&#xff09; BOOTPROTO"static" 打开网络 ONBOOT"yes" 固定ip IPADDR1…

Vue中使用vue-drag-resize实现窗体可拖拽和随意缩放大小

场景 若依前后端分离版手把手教你本地搭建环境并运行项目&#xff1a; 若依前后端分离版手把手教你本地搭建环境并运行项目_ruoyi本地调式_霸道流氓气质的博客-CSDN博客 在上面的基础上&#xff0c;实现弹窗窗体可移动以及随意缩放大小。 效果如下 注&#xff1a; 博客&am…

面试题(三)

目录 一.Spring 1.Spring IOC & AOP 2.Spring bean (1) 作用域 (2) Spring 中的 bean ⽣命周期 (3) Spring 框架中⽤到了哪些设计模式 二.Mybatis 1.标签 2.Dao接口 3.返回与映射 4.延迟加载 三.Kafka 四.设计模式 1.IO 设计模式 2.Spring 中的设计模式详解…

ctfshow-红包题第二弹

0x00 前言 CTF 加解密合集CTF Web合集 0x01 题目 0x02 Write Up 同样&#xff0c;先看一下有没有注释的内容&#xff0c;可以看到有一个cmd的入参 执行之后可以看到文件代码&#xff0c;可以看到也是eval&#xff0c;但是中间对大部分的字符串都进行了过滤&#xff0c;留下了…

ruoyi-cloud部署

默认你已经安装mysql&#xff0c;nacos&#xff0c;seata&#xff0c;sentinel等&#xff08;没有的可以先找教程安装&#xff09; 1、下载源码&#xff1a;git clone https://gitee.com/zhangmrit/ruoyi-cloud 2、项目依赖导入&#xff0c;选择自己的maven环境等&#xff0c;创…

SpringBootWeb案例 Part 5

4. 配置文件 员工管理的增删改查功能我们已开发完成&#xff0c;但在我们所开发的程序中还一些小问题&#xff0c;下面我们就来分析一下当前案例中存在的问题以及如何优化解决。 4.1 参数配置化 在我们之前编写的程序中进行文件上传时&#xff0c;需要调用AliOSSUtils工具类&…

25岁的健康启程:追逐活力,超越年龄

嗨&#xff0c;亲爱的朋友们&#xff01;今天&#xff0c;我想与大家分享一个深入人心的话题&#xff1a;年龄与健康。最近&#xff0c;有关大公司裁员&#xff0c;并将35岁定为“体能下滑”的临界点的新闻引发了热议。是不是只要到了35岁&#xff0c;身体就会渐渐变差&#xf…

Go framework-Kratos

一、Go framework 框架Github开源时间开源方Kratoshttps://github.com/go-kratos/kratos2019Bilibiligo-kithttps://github.com/go-kit/kit/2015团队开源go-zerohttps://github.com/tal-tech/go-zero2020团队开源TarsGohttps://github.com/TarsCloud/TarsGo2018腾讯Jupiterhtt…

免费清理电脑:删除垃圾文件以提升电脑性能

求助&#xff01;电脑上没有可用空间 ​“我只在电脑上存储了大约100张照片&#xff0c;为什么我的硬盘空间已满&#xff1f;电脑运行速度也变得越来越慢&#xff0c;要疯了&#xff01;现在我想安装更新的驱动程序。我可以释放磁盘空间吗&#xff1f;有免费的Windows电脑清…

数组-C语言(初阶)

目录 一、一维数组的创建和初始化 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 二、二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 三、数组越界 四、数组作为函数参数 4.1 数组名 4.2 冒泡排序…

FreeRTOS 查找最高优先级的就绪任务源码分析

一、就绪任务列表 就绪列表 pxReadyTasksLists[ configMAX_PRIORITIES ] 是一个数组&#xff0c; 数组里面存的是就绪任务的 TCB&#xff08;准确来说是 TCB 里面的 xStateListItem 节点&#xff09; &#xff0c;数组的下标对应任务的优先级&#xff0c;优先级越低对应的数组…

Android studio 点击按钮 (跳转界面)

Android studio 点击按钮 &#xff08;跳转界面&#xff09; 问题描述 首先&#xff0c;我们有两个Java文件和与之绑定的xml文件。此处以HistoryActivity.java&#xff0c;activity_history.xml 和 EventDetail.java&#xff0c;activity_event_detail.xml为例子。我们要实现…

05-Numpy基础-用于数组的文件输入输出

np.save和np.load是读写磁盘数组数据的两个主要函数。默认情况下&#xff0c;数组是以未压缩的原始二进制格式保存在扩展名为.npy的文件中的&#xff1a; 如果文件路径末尾没有扩展名.npy&#xff0c;则该扩展名会被自动加上。然后就可以通过np.load读取磁盘上的数组&#xff1…

NAT网关

NAT网关 NAT网关(NAT Gateway)是一种网络地址转换服务&#xff0c;提供NAT代理(SNAT和DNAT)能力。阿里云NAT网关分为公网NAT网关和VPC NAT网关&#xff1a; ■ 公网NAT网关提供公网地址转换服务 ■ VPC NAT网关提供私网地址转换服务 公网NAT网关 公网NAT网关是一款针对公网访…

Matlab分割彩色图像

彩色图像 彩色图像除有亮度信息外&#xff0c;还包含有颜色信息。以最常见的RGB&#xff08;红绿蓝&#xff09;彩色空间为例来简要说明彩色图像&#xff1a; 彩色图像可按照颜色的数目来划分。例如&#xff0c;256色图像和真彩色图像&#xff08;2的16次方&#xff1d;21677…

解决Android Studio中Plugin version和Gradle version不匹配的问题

生命中最艰难的那段路是要自己一个人走过来的&#xff0c;这样&#xff0c;学到更多的是坚强&#xff0c;而不是感动。 《红猪》 前言 导入一个百度云的Demo而已&#xff0c;居然遇到这么多问题&#xff0c;纠结了很久&#xff0c;也查了很多资料&#xff0c;弯弯绕绕了好多路…

Ae 效果:CC Light Wipe

过渡/CC Light Wipe Transition/CC Light Wipe CC Light Wipe&#xff08;CC 光线擦除&#xff09;可以为图层添加指定形状&#xff08;圆形、门形和方形&#xff09;的光效&#xff0c;并通过光效形状的开合来实现擦除过渡。 可以为要过渡到的图层添加“曝光度”等效果&#x…

记Flask-Migrate迁移数据库失败的两个Bug——详解循环导入问题

文章目录 Flask-Migrate迁移数据库失败的两个Bug1、找不到数据库&#xff1a;Unknown database ***2、迁移后没有效果&#xff1a;No changes in schema detected. Flask-Migrate迁移数据库失败的两个Bug 1、找不到数据库&#xff1a;Unknown database ‘***’ 若还没有创建数…

有哪些前端调试和测试工具? - 易智编译EaseEditing

前端开发调试和测试工具帮助开发人员在开发过程中发现和修复问题&#xff0c;确保网站或应用的稳定性和性能。以下是一些常用的前端调试和测试工具&#xff1a; 调试工具&#xff1a; 浏览器开发者工具&#xff1a; 现代浏览器&#xff08;如Chrome、Firefox、Safari等&#…