SpringBoot通过自定义注解实现日志打印
目录
前言:
正文
一.Spring AOP
1.JDK动态代理
2.Cglib动态代理
使用AOP主要的应用场景:
SpringBoot通过自定义注解实现日志打印
一.Maven依赖
二.ControllerMethodLog.class自定义注解
三.Spring AOP切面方法的执行顺序
四.ControllerMethodLogAspect.class:用于打印日志的切面定义类
前言:
在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢?
正文
一.Spring AOP
Spring AOP 即面向切面,是对OOP面向对象的一种延伸。
AOP机制可以让开发者把业务流程中的通用功能抽取出来,单独编写功能代码。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把独立编写的功能代码切入到流程的合适位置。
Spring AOP的实现方式
1.JDK动态代理
- 类对象必须实现接口
- JDK动态代理,背后是借助Java多态的特性,因为JDK动态代理生成的class文件已经继承了Proxy,而Java是单继承的,不能继承目标对象,只能实现目标对象(涉及向上转型),所以是基于JDK动态代理是基于接口的。
JDK动态代理主要涉及两个类:
- InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
- Proxy 利用 InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。
2.Cglib动态代理
- Cglib是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 Java 类与实现 Java 接口,CgLib 封装了asm,可以再运行期动态生成新 的 class。
特别要注意的是:
- 目标类实现接口的情况下使用JDK动态代理,没有实现接口的情况下使用Cglib动态代理。
- 可以使用ProxyTargetClass = true,强制所有都使用Cglib动态代理。
- Cglib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;但是Cglib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
- 对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用Cglib动态代理,反正,则比较适用JDK动态代理。
使用AOP主要的应用场景:
- Authentication 权限检查
- Caching 缓存
- Context passing 内容传递
- Error handling 错误处理
- Lazy loading 延迟加载
- Debugging 调试
- logging, tracing, profiling and monitoring日志记录,跟踪,优化,校准
- Performance optimization性能优化,效率检查
- Persistence 持久化
- Resource pooling资源池
- Synchronization同步
- Transactions 事务管理
SpringBoot通过自定义注解实现日志打印
一.Maven依赖
<!--lombok-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.2</version><optional>true</optional>
</dependency><!--Spring AOP-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二.ControllerMethodLog.class自定义注解
@Retention: 用来修饰注解,是注解的注解,称为元注解。
@Target:用来说明对象的作用范围
@Documented:用来做标记使用
/**
* 自定义注解用于打印Controller层方式日志
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ControllerMethodLog {
}
这里特别讲一下@Retention,按生命周期来划分可分为3类:
- RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃(运行时去动态获取注解信息);
- RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期(在编译时进行一些预处理操作);
- RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在(做一些检查性的操作);
这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
三.Spring AOP切面方法的执行顺序
这里简单介绍一下,切面的执行方法和其执行顺序:
- @Around 通知方法将目标方法封装起来
- @Before 通知方法会在目标方法调用之前执行
- @After 通知方法会在目标方法返回或者异常后执行
- @AfterReturning 通知方法会在目标方法返回时执行
- @Afterthrowing 通知方法会在目标方法抛出异常时执行
这里以一个返回正常的情况为例:(异常替换最后一步即可)
四.ControllerMethodLogAspect.class:用于打印日志的切面定义类
注意要在启动类扫描这个class,并且添加 @EnableAspectJAutoProxy(proxyTargetClass = true)
@Slf4j
@Component
@Aspect
public class ControllerMethodLogAspect {@Pointcut("@annotation(com.xiyuan.demo.annotation.ControllerMethodLog)")public void pointCut() {}/*** 在切点运行前执行该方法*/@Before("pointCut()")public void doBefore(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();ControllerMethodLog annotation = method.getAnnotation(ControllerMethodLog.class);if (Objects.isNull(annotation)) {return;}String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();log.info("start {}:入参:{}", methodName, JSON.toJSONString(joinPoint.getArgs()));}/*** 在切点运行后,无异常时执行该方法** @param joinPoint* @param result*/@AfterReturning(value = "pointCut()", returning = "result")public void afterReturn(JoinPoint joinPoint, Object result) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();ControllerMethodLog annotation = method.getAnnotation(ControllerMethodLog.class);if (Objects.isNull(annotation)) {return;}String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();log.info("end {}:响应:{}", methodName, JSON.toJSONString(result));}}
验证
getUserById:根据id获取用户的信息
@GetMapping("/getUserById")
@ApiOperation(value = "根据用户id获取用户")
@ControllerMethodLog
public ResponseResult getUserById(@RequestParam(name = "id", required = true) String id) {UserInfoPojo userInfoPojo = userService.getUserById(id);return ResponseResult.success(userInfoPojo, ConstantsUtil.QUERY_SUCCESS);
}
Swagger接口信息如下:
IDEA控制台打印信息如下:
相关文章:

SpringBoot通过自定义注解实现日志打印
目录 前言: 正文 一.Spring AOP 1.JDK动态代理 2.Cglib动态代理 使用AOP主要的应用场景: SpringBoot通过自定义注解实现日志打印 一.Maven依赖 二.ControllerMethodLog.class自定义注解 三.Spring AOP切面方法的执行顺序 四.ControllerMethodL…...
代码随想录算法训练营第七天 |151.翻转字符串里的单词
今天是代码随想录的第七天,写了力扣的151.翻转字符串里的单词; 之后或许还要再琢磨琢磨 代码随想录链接 力扣链接 151.翻转字符串里的单词,代码如下: # class Solution: # def reverseWords(self, s: str) -> str: # …...
【WebRTC---源码篇】(十:一)WEBRTC 发送视频RTP包
RTPSenderVideo在整个框架中起到重要的作用,它把采集的数据进行编码,并且在流程中会进行将编码后的数据进行RTP打包,最后发送到网络层 RTPSenderVideo::SendVideo //对编码数据打包 bool RTPSenderVideo::SendVideo(int payload_type,absl::optional<VideoCodecType>…...

cmd 90 validate error!(达梦数据库日志报错)
达梦数据库报错 error-cmd 90 validate error! 环境介绍1 解决办法 环境介绍 某生产环境数据库启动后,dm_实例名_202309.log,偶尔报错cmd 90 validate error! 1 解决办法 接口用错了,消息非法,比如用 6 的 JDBC 连 7 或 7 的 …...

Shape Completion Enabled Robotic Grasping
摘要-这项工作提供了一个架构,使机器人能够通过形状完成抓取规划。形状完成是通过使用3D卷积神经网络(CNN)来完成的。该网络是在我们自己的新的开源数据集上训练的,该数据集包含了从不同视角捕获的超过44万个3D样本。运行时,从单个视角捕获的…...

【C++】构造函数意义 ( 构造函数显式调用与隐式调用 | 构造函数替代方案 - 初始化函数 | 初始化函数缺陷 | 默认构造函数 )
文章目录 一、构造函数意义1、类的构造函数2、构造函数显式调用与隐式调用3、构造函数替代方案 - 初始化函数4、初始化函数缺陷5、默认构造函数6、代码示例 - 初始化函数无法及时调用 一、构造函数意义 1、类的构造函数 C 提供的 构造函数 和 析构函数 作为 类实例对象的 初始化…...

VMware16安装ghost版win7
文章目录 准备工作GHO 文件装机工具 新建虚拟机配置虚拟机还需要一个 CD/DVD PE 安装步骤分区还原挂载 CD/DVD开始还原 还原之后 准备工作 GHO 文件 可以去百度搜索这种文件,我这里是从系统之家下载的deepin win7 ghost 系统 装机工具 因为下载的 ghost 版的 w…...
项目集成swagger,访问不到swagger页面
项目集成swagger 文章目录 前言一、背景二、问题复现1.Full authentication is required to access this resource2.Illegal DefaultValue null for parameter type integer 总结 前言 项目集成swagger这个没啥好整的,maven项目就在pom文件导入依赖,ser…...
微信小程序怎么隐藏顶部导航栏(navigationBar)变透明的解决方案
怎么隐藏小程序顶部导航栏(navigationBar)? 官网说:Navigation是小程序的顶部导航组件,当页面配置 navigationStyle 设置为 custom 的时候可以使用此组件替代原生导航栏。 那么,我们就知道这种效果是可以…...

RabbitMQ基础概念-02
RabbitMQ是基于AMQP协议开发的一个MQ产品, 首先我们以Web管理页面为 入口,来了解下RabbitMQ的一些基础概念,这样我们后续才好针对这些基础概念 进行编程实战。 可以参照下图来理解RabbitMQ当中的基础概念: 虚拟主机 virtual hos…...

从构建者到设计者的低代码之路
低代码开发技术,是指无需编码或通过少量代码就可以快速生成应用程序的工具,一方面可降低企业应用开发人力成本和对专业软件人才的需求,另一方面可将原有数月甚至数年的开发时间成倍缩短,帮助企业实现降本增效、灵活迭代。那么&…...

Linux创建进程 及父子进程虚拟空间 多进程GDB调试
父子进程的资源是读时共享,写时拷贝,用到某一个资源,比如说改变变量的值的时候才去拷贝这个变量到一个独立的空间 父子进程的关系: 区别: 1.fork()函数的返回值不同 父进程中:>…...

uni-app 之 表格设置
uni-app 之 表格设置 image.png <view style"padding: 3%; border: #1296db;"><table style"width: 100%; border-collapse: collapse; "><tr style"height: 50px;border: 2px solid;border-color: #F7F7F7;"><td style&qu…...
Linux易混淆知识点
1. 使用 vi 编辑某个文件时,执行删除某行/某几行的操作: dd:删除光标所在行; 6dd:表示删除当前行开始 6 行数据。 ndd: 删除当前行开始的连续 n 行; n1,n2d:删除 n1 到 n2 行&#x…...

移植FlashDB、SFUD到STM32f407
个人上篇文章 搭建STM32F407的SPI-Flash(基于STM32CubeMX)_小刚学長的博客-CSDN博客 主要是解决STM32CubeMX这边的配置,对code端侧是简单介绍了下 实际项目上一般都是拿片外flash存储一些东西,比如一些比较多的配置、参数&…...

【算法基础】时间复杂度和空间复杂度
目录 1 算法的评价 2 算法复杂度 2.1 时间复杂度(Time Complexity) 2.1.1 如何计算时间复杂度: 2.1.2 常见的时间复杂度类别与示例 2.2 空间复杂度 2.2.1 如何计算空间复杂度 2.2.2 常见的空间复杂度与示例 3 时间复杂度和空间复杂度…...
解决微信小程序不支持TextEncoder/TextDecoder对象
问题描述:在使用小程序开发者工具开发小程序中使用到了CRC算法,其中有一行代码使用到了TextEncoder对象,在开发工具中一切正常,到手机上会报出错误错误如下: MiniProgramError TextEncoder is not defined ReferenceEr…...

Qt下SVG格式图片应用
SVG格式图片介绍 svg格式图片又称矢量图,该种格式的图片不同于png等格式的图片,采用的并不是位图的形式来组织图片,而是采用线条等组织图片,svg格式是图片的文件格式是xml,可以通过文件编译器打开查看svg格式内容。 …...

python异常处理
参考语法:https://docs.python.org/zh-cn/3/tutorial/errors.html 在编写代码的时候,如果你写的程序出现报错,程序就会停止运行,后面的代码就不再执行。 如果程序发生错误,可以在代码中添加异常处理,保证程…...
go get命令不再具有安装功能
go get功能呢 一直以来,我们知道go get命令可以借助代码管理工具通过远程拉取或更新代码包及其依赖包,并自动完成编译和安装。整个过程就像安装一个App一样简单。 go get命令可以动态获取远程代码包,命令在内部实际上分成了两步操作&#x…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...

JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
RLHF vs RLVR:对齐学习中的两种强化方式详解
在语言模型对齐(alignment)中,强化学习(RL)是一种重要的策略。而其中两种典型形式——RLHF(Reinforcement Learning with Human Feedback) 与 RLVR(Reinforcement Learning with Ver…...