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…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
