Spring AOP实战--之优雅的统一打印web请求的出参和入参

  • 背景介绍

由于实际项目内网开发,项目保密,因此本文以笔者自己搭建的demo做演示,方便大家理解。

在项目开发过程中,团队成员为了方便调试,经常会在方法的出口和入口处加上log输出,由于每个人的log需求和输出方式不一样,在测试环境还好,但是上线后导致项目的日志输出特别的杂乱,有时候想要根据日志排查问题就特别地费劲。下面demo是项目中典型的日志输出方式

对于上面的日志打印位置和输出,其实是特别随意不规范的

  • 例如controller层和service层都对请求的入参进行了打印,输出没有什么明显的改变,这个一般可以只保留一个
  • 日志的整个请求缺少链路追踪,如果多个请求过来都打印分不清哪个是哪个

鉴于存在以上的不足,笔者痛下决心决定对日志打印进行改造。

  • 架构思路

由于笔者的项目是微服务集群架构,但是单个服务是遵循MVC分层架构的,如下图所示:

鉴于这样的分层结构,笔者决定从controller层下手,使用spring AOP封装统一的入参和出参日志打印,以规范和解决项目中日志输出的乱象。

  • Spring AOP 核心概念

开始撸代码之前先简单回顾下Spring AOP的相关知识点

1、切面(aspect):切面就是对横切关注点的抽象,被@Aspect标记的类
2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
3、连接点(joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring
中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。
4、切入点(pointcut):对连接点进行拦截的定义,可以是切点表达式,也可以是注解
5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
6、目标对象:代理的目标对象
7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程

我们编码比较关注的就是 切点(pointcut) 和 通知 (advice)

下面我们开始撸代码!

  • 代码实现

如果对AOP编码不熟悉的同学,可以移步官方文档:

https://docs.spring.io/spring-framework/reference/core/aop.html

下面上我的切面类的代码

package com.cjt.demo.springaopdemo.aop;import com.cjt.demo.springaopdemo.utils.JsonUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;import java.util.UUID;/****************************************************** @package com.cjt.demo.springaopdemo.aop* @class WebParamAspect* @author caojiantao* @datetime 2024/6/21 14:53* @describe WEB请求参数打印切面类*           https://docs.spring.io/spring-framework/reference/core/aop.html****************************************************/
@Aspect
@Component
@ConditionalOnClass(value = {ObjectMapper.class})
public class WebParamAspect {/*** 全局日志记录*/private static final Logger PARAM_LOG = LoggerFactory.getLogger(WebParamAspect.class);/*** 定义切点: execution ,with,target等,可以参考官方文档* https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html*/@Pointcut(value = "execution(public * com.cjt.demo.springaopdemo.controller..*(..))")public void cut() {}/*** 使用环绕通知:可以拿到请求前和请求后的参数,同时打印** @param joinPoint 连接点* @return*/@Around(value = "cut()")public Object printWebParam(ProceedingJoinPoint joinPoint) throws Throwable {// 可以加入接口记录时间,方法进入的时间long start = System.currentTimeMillis();// 获取连接点方法签名,可以从中解析得到关于该方法的许多信息MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();// 方法所在类名称String declaringTypeName = methodSignature.getDeclaringTypeName();// 方法名String methodName = methodSignature.getMethod().getName();// 生成唯一的交易编码,便于观察出参和入参String uuid = UUID.randomUUID().toString();// 获取参数Object[] args = joinPoint.getArgs();String jsonString = JsonUtil.toJSONString(args);// 判断是否开启打印,打印请求信息if (PARAM_LOG.isInfoEnabled()) {PARAM_LOG.info("TRACE:{} , 请求服务{} - 请求方法{}, 请求参数: {}", uuid, declaringTypeName, methodName, jsonString);}Object result = null;try {// 执行代理类的实现逻辑result = joinPoint.proceed();} catch (Throwable throwable) {if (PARAM_LOG.isErrorEnabled()) {PARAM_LOG.error("TRACE:{} , 请求服务{} - 请求方法{}, 发生异常了,原因是: {}", uuid, declaringTypeName, methodName, throwable.getMessage(), throwable);}throw throwable;}// 方法结束的时间long end = System.currentTimeMillis();// 判断是否开启打印,打印结果信息if (PARAM_LOG.isInfoEnabled()) {PARAM_LOG.info("TRACE:{} , 请求服务{} - 请求方法{} , 请求耗时 : {} ms , 请求结果: {}", uuid, declaringTypeName, methodName, (end - start), jsonString);}return result;}}
  • 切点使用了 execution表达式,只拦截controller层,关于如何定义切点可以参考官方文档:

​​​​​​​Declaring a Pointcut :: Spring Framework

  • 通知类型使用了环绕通知,可以拿到代理方法执行前后的结果进行显示
  • 日志打印个性化的加入了UUID作为链路跟踪,可以很清晰的看入参和出参,同时在打印出差记录的时候又显示了接口执行的时间。
  • 演示效果

演示效果如下图所示:

可以看到成对的uuid可以明显的定位到一个请求的出参和入参情况,也能直观的看的请求的耗时,另外调用的类和方法也很明确的打印出来了,给后续的日志排查问题定位代码提供的便利,

  • 源码地址

笔者的demo代码使用的是 springboot单体架构

主要技术点: SpringBoot + Sqlite + knife4j + Mybatis 

https://github.com/1989Jiangtao/spring-aop-demo.giticon-default.png?t=N7T8https://github.com/1989Jiangtao/spring-aop-demo.git

Jiangtao/spring-aop-demoicon-default.png?t=N7T8https://gitee.com/caojiangtao1989/spring-aop-demo.git

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

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

相关文章

奔驰EQS SUV升级原厂主动式氛围灯效果展示

以下是一篇关于奔驰 EQs 升级原厂主动氛围灯案例的宣传文案: 在汽车科技不断演进的今天,我们自豪地为您呈现奔驰 EQs 升级原厂主动氛围灯的精彩案例。 奔驰 EQs,作为豪华电动汽车的典范,其卓越品质与高端性能有目共睹。而此次升…

CVPR 2024盛况空前,上海科技大学夺得最佳学生论文奖,惊艳全场

CVPR 2024盛况空前!上海科技大学夺得最佳学生论文奖,惊艳全场! 会议之眼 快讯 2024 年 CVPR (Computer Vision and Pattern Recogntion Conference) 即国际计算机视觉与模式识别会议,于6月17日至21日正在美国西雅图召…

手把手教你java CPU飙升300%如何优化

背景 今天有个项目运行一段时间后,cpu老是不堪负载。 排查 top 命令 TOP 命令 top t 按cpu 排序 top m 按内存使用率排序 从上面看很快看出是 pid 4338 这个进程资源消耗很高。 top -Hp pid top -Hp 4338 找到对应线程消耗的资源shftp cpu占用进行排序&#xf…

【Java】已解决java.net.ProtocolException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.net.ProtocolException异常 在Java的网络编程中,java.net.ProtocolException异常通常表示在网络通信过程中,客户端或服务器违反了某种协议规则。…

Java 中引用类型的参数传递

了解 Java 中引用类型的参数传递 Java 是一种面向对象的编程语言,它的参数传递机制一直是新手和有经验的开发者之间讨论的热点话题。特别是当涉及到引用类型时,理解其工作原理变得尤为重要。下面我们将详细探讨 Java 中引用类型的参数传递机制。 Java …

计算机组成原理 | 计算机系统概述

CPI:(Clockcycle Per Instruction),指每条指令的时钟周期数。 时钟周期:对CPU来说,在一个时钟周期内,CPU仅完成一个最基本的动作。时钟脉冲是计算机的基本工作脉冲,控制着计算机的工作节奏。时钟周期 是一个时钟脉冲所…

【2024Python教程】-MongoDB数据库连接

MongoDB数据库连接实战 MongoDB数据库连接 首先,通过以下代码连接到本地的MongoDB: client MongoClient(mongodb://localhost:27017/)然后,创建或切换到名为pdf_contents的数据库: db client[pdf_contents]在pdf_contents数据库中,创建或切换到名为…

除了百度,还有哪些搜索引擎工具可以使用

搜索引擎成是我们获取知识和信息不可或缺的工具。百度作为国内最大的搜索引擎,全球最大的中文搜索引擎,是许多人的首选。那么除了百度,还有哪些搜索引擎可以使用呢?小编就来和大家分享国内可以使用的其他搜索工具。 1. AI搜索 AI…

微信公众号开发,uploadImg上传图片接口40001错误解决办法

使用微信公众平台,公众号接口uploadImg上传图片,的时候,access_token明明是对的,appsecret也是对的,但是一直返回40001错误,获取access_token时AppSecret错误,或者access_token无效,…

梯度提升决策树(GBDT)的训练过程

以下通过案例(根据行为习惯预测年龄)帮助我们深入理解梯度提升决策树(GBDT)的训练过程 假设训练集有4个人(A、B、C、D),他们的年龄分别是14、16、24、26。其中A、B分别是高一和高三学生&#x…

大模型时代,新手和程序员如何转型入局AI行业?

在近期的全国两会上,“人工智能”再次被提及,并成为国家战略的焦点。这一举措预示着在接下来的十年到十五年里,人工智能将获得巨大的发展红利。技术革命正在从“互联网”向“人工智能”逐步迈进,我将迎来新一轮技术革新和人才需求…

ASP.NET Core 6.0 启动方式

启动方式 Visualstudio 2022启动 IIS Express IIS Express 是一个专为开发人员优化的轻型独立版本的 IIS。 借助 IIS Express,可以轻松地使用最新版本的 IIS 开发和测试网站。 控制台版面 直接在浏览器输入监听的地址,监听的是 http://localhost:5137 脚本启动 dotnet run…

C++11 右值引用和移动语义

目录 1.左值引用和右值引用 2.右值引用使用场景(移动语义)和意义 3.右值引用引用左值及其一些更深入的使用场景分析 4.完美转发 1.左值引用和右值引用 传统的C语法中就有引用的语法,而C11中新增了的右值引用语法特性,所以从现…

如何定义一个单选按钮?

在HTML中&#xff0c;你可以使用<input>元素来定义一个单选按钮&#xff08;radio button&#xff09;。单选按钮通常用于从一组选项中选择一个选项。为了将多个单选按钮组合在一起&#xff0c;使它们成为一组&#xff08;即&#xff0c;一次只能选择其中一个&#xff09…

Verilog:【8】基于FPGA实现SD NAND FLASH的SPI协议读写

在此介绍的是使用FPGA实现SD NAND FLASH的读写操作&#xff0c;以雷龙发展提供的CS创世SD NAND FLASH样品为例&#xff0c;分别讲解电路连接、读写时序与仿真和实验结果。 目录 1 视频讲解 2 SD NAND FLASH背景介绍 3 样品申请 4 电路结构与接口协议 4.1 SD NAND 4.2 SD NAND测…

free命令——显示系统内存使用情况

free命令的功能是显示系统内存使用情况&#xff0c;包含物理内存和交换内存的总量、使用量和空闲量。 语法格式&#xff1a;free [选项] 常用选项及含义 选项含义-b以字节B为单位显示内存和交换内存的容量使用情况-k以KB为单位显示内存和交换内存的容量使用情况-m以MB为单位…

机器学习算法的电影推荐系统以及票房预测系统

一、实验概述 1. 实验目标 本项目希望基于电影数据集&#xff0c;依据电影的简介、关键词、预算、票房、用户评分等特征来对电影进行分析&#xff0c;并完成以下任务&#xff1a; 对电影特征的可视化分析对电影票房的预测多功能个性化的电影推荐算法 2. 数据集 针对票房预…

AIGC-CVPR2024best paper-Rich Human Feedback for Text-to-Image Generation-论文精读

Rich Human Feedback for Text-to-Image Generation斩获CVPR2024最佳论文&#xff01;受大模型中的RLHF技术启发&#xff0c;团队用人类反馈来改进Stable Diffusion等文生图模型。这项研究来自UCSD、谷歌等。 在本文中&#xff0c;作者通过标记不可信或与文本不对齐的图像区域&…

内网环境使用Docker部署Qwen2模型

背景介绍 在我参与的一个国企项目中,我们基于大语言模型开发了一些应用,但是甲方公司所有的资源环境都是纯内网。更为有趣的是,甲方公司已自主搭建并运行着一套百度机器学习平台(BML),客户要求所有的大模型部署必须依托于现有的BML平台进行,而非独立构建全新的基础设施…

编程机器人方阵怎么编程的:深入解析其编程逻辑与实现

编程机器人方阵怎么编程的&#xff1a;深入解析其编程逻辑与实现 在科技日新月异的今天&#xff0c;编程机器人方阵作为自动化和智能化的杰出代表&#xff0c;吸引了无数人的目光。那么&#xff0c;这些机器人方阵究竟是如何进行编程的呢&#xff1f;本文将从四个方面、五个方…