springboot 注解+AOP实现接口方法出入参打印

注解+AOP实现接口方法出入参打印

    • 0、前言
    • 1、自定义注解
    • 2、定义AOP
    • 3、封装日志打印方法

0、前言

针对加入spring bean容器的实例方法,通过注解+aop实现打印方法调用的出入参,通过注解可以配置是否打印出入参,出入参中的单列集合或双列集合是否打印,选择非全量打印只会打印集合的大小

1、自定义注解

import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface LogFormatConsole {/*** 入参* 是否打印全量参数   true-集合类型全量打印 false-集合类型只打印数量*/boolean isFullPrintReq() default true;/*** 出参* 是否打印全量参数  true-集合类型全量打印 false-集合类型只打印数量*/boolean isFullPrintResp() default false;/*** 是否打印入参 默认打印*/boolean isPrintReqParam() default true;/*** 是否打印出参 默认打印*/boolean isPrintRespParam() default true;
}

2、定义AOP

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.lqt.hospital.platform.common.annotation.LogFormatConsole;
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.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;import static com.lqt.hospital.platform.common.utils.ToolsUtil.*;/*** @DATE: 2023/12/28 18:45* @Author: 小爽帅到拖网速*/
@Aspect
@Component
public class FormatConsoleAop {/*** 日志打印切入点*/@Pointcut("@within(com.lqt.hospital.platform.common.annotation.LogFormatConsole) || @annotation(com.lqt.hospital.platform.common.annotation.LogFormatConsole)")private void consolePointCut() {}@Around("consolePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {// 获取目标签名和方法MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();// 目标方法名String methodName = method.getName();// 获取目标方法注解LogFormatConsole anno = method.getAnnotation(LogFormatConsole.class);// 是否全量打印集合参数-入参boolean fullPrintReq = anno.isFullPrintReq();// 是否全量打印集合参数-出参boolean fullPrintResp = anno.isFullPrintResp();// 是否打印入参boolean printReqParam = anno.isPrintReqParam();// 是否打印出参boolean printRespParam = anno.isPrintRespParam();if (printReqParam) {// 打印入参// 获取方法的参数列表String[] parameterNames = signature.getParameterNames();// 获取参数值Object[] args = point.getArgs();if (args.length > 0) {// 构建请求入参集Map<String, Object> reqParamMap = buildReqParamMap(fullPrintReq, parameterNames, args);// 打印请求入参formatConsoleInfoLogReq(methodName, reqParamMap, (boolean) reqParamMap.get("fullPrintReq"));}}// 执行目标方法Object result = point.proceed();if (printRespParam) {// 打印出参formatConsoleInfoLogResp(methodName, result, fullPrintResp);}return result;}/*** 构建请求入参集*/private Map<String, Object> buildReqParamMap(boolean fullPrintReq, String[] parameterNames, Object[] args) {String sizeStr;Map<String, Object> reqParamMap = Maps.newHashMap();boolean nofullPrintFlag = false;for (int i = 0; i < Math.min(parameterNames.length, args.length); i++) {// 参数值final Object arg = args[i];// 参数名final String parameterName = parameterNames[i];if (ObjectUtil.hasEmpty(arg, parameterName)) {continue;}if (arg instanceof HttpServletRequest) {// 提取HttpServletRequest有效信息putReqHeaderIntoMap(reqParamMap, (HttpServletRequest) arg);} else if (!fullPrintReq) {// 标识部分不全量打印nofullPrintFlag = true;// 针对集合参数,打印集合大小if (arg instanceof Collection) {// 单列集合sizeStr = "size=" + ((Collection<?>) arg).size();reqParamMap.put(parameterName, sizeStr);}// else if (args[i] instanceof Map) {//     // 双列集合//     sizeStr = "size=" + ((Map<?, ?>) args[i]).size();//     reqParamMap.put(parameterNames[i], sizeStr);// }else {// pojoreqParamMap.put(parameterName, arg);}} else {// pojoreqParamMap.put(parameterName, arg);}}if (!fullPrintReq) {// 标识部分不全量打印fullPrintReq = nofullPrintFlag;}reqParamMap.put("fullPrintReq", fullPrintReq);return reqParamMap;}/*** 提取HttpServletRequest有效信息*/private void putReqHeaderIntoMap(Map<String, Object> map, HttpServletRequest request) {String unitId = request.getHeader("UnitId");String userId = request.getHeader("UserId");map.put("unitId", unitId);map.put("userId", userId);}}

3、封装日志打印方法

@Log4j2
public class ToolsUtil {  /*** 格式化打印info日志** @param methodName     方法名* @param mainMsg        主信息* @param param          参数* @param isFullPrint 是否不打印全量参数  true-集合类型只打印数量 false-集合类型全量打印*/public static void formatConsoleInfoLog(String methodName, String mainMsg, Object param, boolean isFullPrint) {if (ObjectUtil.hasEmpty(methodName, param)) {return;}String paramStr = null;if (param instanceof Number) {// 数值类型paramStr = param.toString();} else if (!isFullPrint) {// 不进行全量打印if (param instanceof Collection) {// 单列集合paramStr = String.valueOf(((Collection<?>) param).size());mainMsg = mainMsg + "size";}// else if (param instanceof Map) {//     // 双列集合//     paramStr = String.valueOf(((Map<?, ?>) param).size());//     mainMsg = mainMsg + "size";// }else if (param instanceof Result) {// 针对Result data里边存在的单列或双列集合进行处理Object data = ((Result<?>) param).getData();if (data instanceof Collection) {data = "size=" + ((Collection<?>) data).size();}// else if (data instanceof Map) {//     data = "size=" + ((Map<?, ?>) data).size();// }((Result) param).setData(data);paramStr = JSONUtil.toJsonStr(param);}} else {// pojoparamStr = JSONUtil.toJsonStr(param);}log.info("[{}] {}:{}", methodName, mainMsg, paramStr);}/*** 格式化打印info日志-请求入参** @param methodName     方法名* @param param          参数* @param isFullPring 是否不打印全量参数  true-集合类型只打印数量 false-集合类型全量打印*/public static void formatConsoleInfoLogReq(String methodName, Object param, boolean isFullPring) {formatConsoleInfoLog(methodName, "请求入参", param, isFullPring);}/*** 格式化打印info日志-请求出参** @param methodName     方法名* @param param          参数* @param isFullPring 是否不打印全量参数  true-集合类型只打印数量 false-集合类型全量打印*/public static void formatConsoleInfoLogResp(String methodName, Object param, boolean isFullPring) {formatConsoleInfoLog(methodName, "接口出参", param, isFullPring);}
}

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

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

相关文章

计算机体系结构期末复习流程大纲

1.存储器和cache 存储器的容量、速度与价格之间的要求是相互矛盾的&#xff0c;速度越快&#xff0c;没bit位价格越高&#xff0c;容量越大&#xff0c;速度越慢&#xff0c;目前主存一般有DRAM构成。 处理器CPU访问存储器的指标&#xff1a; 延迟时间&#xff08;Latency&am…

【C++】—— 工厂模式详解

目录 &#xff08;一&#xff09;工厂模式的特点 &#xff08;二&#xff09;工厂模式分类 1、简单工厂模式 2、工厂方法模式 3、抽象工厂模式 &#xff08;三&#xff09;总结与回顾 &#xff08;一&#xff09;工厂模式的特点 1、优势 ⼯⼚模式是⼀种创建型设计模式&a…

快速入门Visual Studio 2022开发.Net Framework研发环境指南

IDE工具 Visual Studio 2022 Vs2022企业版 - VisualStudioSetup.exe Visual Studio Code VSCodeUserSetup-x64-1.66.2.exeVSCodeUserSetup-x64-1.67.0-insider.exe IDE环境 编程字体YaHei.Consolas YaHei.Consolas.1.12.ttf IDE插件 Visual Studio Code常用插件 Chinese…

STM32单片机使用STM32CubeMX和Simulink进行联合开发-1

STM32单片机使用STM32CubeMX和Simulink进行联合开发-1 使用Simulink中的STM32扩展功能包配合STM32CubeMX进行联合开发,包含环境配置和实际应用教程。 目录 STM32单片机使用STM32CubeMX和Simulink进行联合开发-1一、环境配置1.安装扩展功能包2.安装STM32-MAT包二、创建STM32Cu…

django项目基础后端功能使用

参考材料 Django新手项目实例-CSDN博客 一、django安装 pip3 install django 二、django项目新建 在目标目下执行 django-admin startproject testdjgo 执行完成后生成对应项目路径 三、django路由功能编写 /xxx/urls.py中编写路由信息&#xff0c;并且把路由转发到对应…

说出来你别不信,盲订问界M9的原因 你们想错了,他们只图这个

文|AUTO芯球 作者|李瑞 怎么还有人说华为是骗子&#xff1f; 华为一张海报说问界M9上市6天&#xff0c;大定超过3万台。有些人就说这是假的&#xff0c;反正没第三方数据&#xff0c;华为可以随便写。 我去&#xff0c;我作为一名大定问界M9的车主&#xff0c;就奉劝哪些黑子…

Linux篇之Centos中将系统时间设置为本地时间

要在 CentOS 上将系统时间设置为本地时间&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.首先&#xff0c;你需要确定你想要设置的本地时间。例如&#xff0c;如果你想要将系统时间设置为当前时间&#xff08;假设是北京时间&#xff09;&#xff0c;则可以使用以下命令获…

5.vue学习笔记(数组变化的侦测+计算属性+Class绑定)

文章目录 1.数组变化的侦测1.1.变更方法1.2.替换一个数组 2.计算属性计算属性缓存vs方法 3.Class绑定3.1.绑定对象3.2.多个对象的绑定形式3.3.绑定数组3.4.数组与对象 1.数组变化的侦测 1.1.变更方法 vue能够侦听响应式数组的变更方法&#xff0c;并在它们被调用时出发相关的…

Taro+vue3 实现电影切换列表

1.需求 我们在做类似于猫眼电影的小程序或者H5 的时候 我们会做到那种 左右滑动的电影列表&#xff0c;这种列表一般带有电影场次 2.效果 3.说明 这种效果在淘票票 猫眼电影上 都有的 &#xff0c;一般电影类型的H5 或者小程序 这个是都有的 第一是好看 第二是客观性比较好 …

【Linux】【开发】Linux module名和C语言文件名相同导致的编译问题

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;Linux技术&…

Mysql InnoDB行锁深入理解

Record Lock记录锁 Record Lock 称为记录锁&#xff0c;锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的&#xff1a; 当一个事务对一条记录加了 S 型记录锁后&#xff0c;其他事务也可以继续对该记录加 S 型记录锁&#xff08;S 型与 S 锁兼容&#xff09;&#xff0c;…

物联网与智能家居:跨境电商与未来生活的融合

物联网&#xff08;Internet of Things&#xff0c;IoT&#xff09;和智能家居技术正迅速崛起&#xff0c;成为跨境电商领域的创新引擎。这两者的巧妙结合不仅为消费者提供更智能、便捷的生活方式&#xff0c;同时也为电商平台和制造商带来了全新的商机。本文将深入探讨物联网与…

Flink之Task重启策略

Task重启策略 1 策略API noRestart 无参数,task失败后不重启,整个job同时失败,默认策略. 代码示例 RestartStrategies.noRestart();fixedDelayRestart 参数注释restartAttempts最大重启次数delayBetweenAttempts重启时间间隔代码示例 // 最多重启5次,每次任务失败后间隔1s重启…

记录实现级联选择器多选功能时主要用到的函数

级联选择器多选功能时主要用到的函数 1、校验所给层级是否有效并且是否为完整路径 // 功能&#xff1a;涉及到级联选择器回显时&#xff0c;需校验是否为完整的路径&#xff0c;是则回显&#xff0c;不是则无无效路径 // options为级联数据&#xff0c;selectedPaths为要校验…

C++模板——(3)类模板

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 勤奋&#xff0c;机会&#xff0c;乐观…

使用pyinstaller打包生成exe(解决gradio程序的打包问题)

解决 [Errno 2] No such file or directory: gradio_client\types.json 问题&#xff0c;不需要手动创建hook文件 解决 FileNotFoundError: [Errno 2] No such file or directory: gradio\blocks_events.pyc 问题&#xff0c;不需要将pyi文件重命名为pyc文件 最终实现gradio程…

卡码网Java基础课 | 7. 摆平积木,8. 奇怪的信

卡码网Java基础课|7. 摆平积木 7. 摆平积木8. 奇怪的信 7. 摆平积木 import java.util.Scanner; import java.util.ArrayList;public class Main{public static void main(String[] args){Scanner sc new Scanner(System.in);while(sc.hasNextInt()){int n sc.nextInt();if(…

【CSS】讲一讲BFC、IFC、GFC、FFC

1. 前言 FC&#xff08;Formatting Contexts&#xff09;&#xff0c;是CSS2.1的一个概念&#xff0c;是页面中的一块渲染区域&#xff0c;具有一套渲染规则&#xff0c;决定FC中子元素如何定位&#xff0c;以及和其他元素的关系和相互作用。在说FC之前说一下文档流。 1.1. 普…

手撕 PCA

PCA&#xff08;Principal Component Analysis&#xff09;&#xff0c;中文名称&#xff1a;主成分分析。迄今为止最流行的降维算法。 假设 n 维空间中的一个单位立方体&#xff0c;易知&#xff1a;一维空间中该立方体中任意两点的距离不超过 1 1 1&#xff0c;二维空间中该…