springboot使用自定义注解实现aop切面日志
作者:原野灬 发布时间:2023-11-11 09:14:48
平时我们在开发过程中,代码出现bug时为了更好的在服务器日志中寻找问题根源,会在接口的首尾打印日志,看下参数和返回值是否有问题。但是手动的logger.info() 去编写时工作量较大,这时我们可以使用AOP切面,为所有接口的首尾打印日志。
实现AOP切面日志一般有两种方式:
1、拦截所有接口controller,在首尾打印日志
2、拦截指定注解的接口,为有该注解的接口首尾打印日志
我们尝试用自定义注解来实现AOP日志的打印,这样拥有更高的灵活性。废话不多说,我们开始
1. 导入切面需要的依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 自定义注解 AOPLog , 指定注解使用在方法上, 指定在运行时有效
Target:描述了注解修饰的对象范围
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述方法变量
TYPE:用于描述类、接口或enum类型
Retention: 表示注解保留时间长短
SOURCE:在源文件中有效,编译过程中会被忽略
CLASS:随源文件一起编译在class文件中,运行时忽略
RUNTIME:在运行时有效
只有定义为 RetentionPolicy.RUNTIME(在运行时有效)时,我们才能通过反射获取到注解,然后根据注解的一系列值,变更不同的操作。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author buer
* @date 2019/12/26
*/
// 指定注解使用在方法上
@Target(ElementType.METHOD)
// 指定生效至运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface AOPLog {
/**
* 指定是否详情显示
* true 显示详情, 默认false
*
* @return
*/
boolean isDetail() default false;
}
3. 设置切面类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
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.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author buer
* @date 2019/12/26
* @description //TODO
*/
// 指定切面类
@Aspect
// 注入容器
@Component
public class AOPLogAspect {
private static Logger log = LoggerFactory.getLogger(AOPLogAspect.class);
/**
* 指定切点, 切点的位置是存在该注解com.xingyun.xybb.demo.annotation.AOPLog
*/
@Pointcut("@annotation(com.xingyun.xybb.demo.annotation.AOPLog)")
public void logPointCut() {
}
/**
* 环绕通知, 该处写具体日志逻辑
*
* @param joinPoint
*/
@Around("logPointCut()")
public void logAround(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取方法名称
String methodName = signature.getName();
// 获取入参
Object[] param = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
for (Object o : param) {
sb.append(o).append("; ");
}
log.info("进入方法[{}], 参数有[{}]", methodName, sb.toString());
String resp = "";
try {
Object proceed = joinPoint.proceed();
resp = JSON.toJSONString(proceed, SerializerFeature.WriteMapNullValue);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 获取方法上的注解,判断如果isDetail值为true,则打印结束日志
Method method = signature.getMethod();
AOPLog annotation = method.getAnnotation(AOPLog.class);
boolean isDetail = annotation.isDetail();
if (isDetail) {
log.info("方法[{}]执行结束, 返回值[{}]", methodName, resp);
}
}
}
4. 编写测试接口, 测试切面日志是否生效
import com.xingyun.xybb.common.response.XyResponseEntity;
import com.xingyun.xybb.demo.annotation.AOPLog;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author buer
* @date 2019/12/26
* @description //TODO
*/
@RestController
public class TestAOPLogController {
// 指定注解@AOPLog
@AOPLog
@GetMapping("/testAOP")
public ResponseEntity<?> testAOPLog() {
return XyResponseEntity.ok();
}
// 指定注解@AOPLog, 同时isDetail = true
@AOPLog(isDetail = true)
@GetMapping("/testAOPLogDetail")
public ResponseEntity<?> testAOPLogDetail() {
return XyResponseEntity.ok();
}
}
5. 分别请求两测试接口
http://localhost:8499/demo/testAOP
http://localhost:8499/demo/testAOPLogDetail
控制台打印出
2019-12-26 14:00:56.336 ***.AOPLogAspect : 进入方法[testAOPLog], 参数有[]
2019-12-26 14:01:00.372 ***.AOPLogAspect : 进入方法[testAOPLogDetail], 参数有[]
2019-12-26 14:01:00.373 ***.AOPLogAspect : 方法[testAOPLogDetail]执行结束, 返回值[{"body":{"retCode":200,"retEntity":null,"retMsg":"OK"},"headers":{},"statusCode":"OK","statusCodeValue":200}]
由此可看出,AOP切面拦截成功,打印出了日志,同时设置了 isDetail = true 时,打印出了结束日志。
自定义注解实现AOP切面打印日志完成。
来源:https://blog.csdn.net/lp2388163/article/details/103714079
猜你喜欢
- 当我们开发spring web应用程序时,对于如 IOException , ClassNotFoundException 之类的检查异常,
- springmvc 使用map接收参数开发过程中有时候我们并不知道前端都会传递哪些参数给到后端. 为方便扩展接口功能, 在请求参数不改变的情
- 初次接触spring-boot的时候,我们经常会看到这样的文章:“
- Java程序设计 图形用户界面【四】按钮组件 JButtonJButton组件表示一个普通的按钮JButton类常用方法方法作用public
- 概述ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。ArrayList不是
- 讲完了inbound事件和outbound事件的传输流程, 这一小节剖析异常事件的传输流程传播异常事件简单的异常处理的场景@Override
- 最近看spring的JDBCTemplete的模板方式调用时,对模板和回调产生了浓厚兴趣,查询了一些资料,做一些总结。回调函数:所谓回调,就
- 在开发中,用到springboot项目,当打包后部署运行时,出现了这个问题,网上搜了好多,又是加META-INF配置,又是加啥的,感觉spr
- 说明在学习jvm相关知识时,一般会讲到类字节码相关内容,为了更清晰的了解类字码具体内容,一般我们会使用javap命令进行查看,但是仍然不够直
- 1. 为什么要进行参数校验在后端进行工作时,需要接收前端传来的数据去数据库查询,但是如果有些数据过于离谱,我们就可以直接把它pass掉,不让
- 项目场景:适用于接口数据敏感信息,比如 明文传输姓名、居住地址、手机号等信息,如果存在明文传输敏感数据问题、及数据泄漏风险,则可使用此方法加
- 本文实例讲述了Java单例模式。分享给大家供大家参考,具体如下:在实际开发的时候会有一些需求,在某个类中只能允许同时存在一个对象。这时就需要
- 简介我们在使用flutter的过程中,有时候需要控制某些组件是否展示,一种方法是将这个组件从render tree中删除,这样这个组件就相当
- 前言本文将带您了解在 Flutter 中制作翻转卡片动画的两个完整示例。第一个示例从头开始实现,第二个示例使用第三方包。闲话少说,让我们动手
- Java synchronized 关键字 可以将一个代码块或一个方法标记为同步代码块。同步代码块是指同一时间只能有一个线程执行的代码,并且
- IDEA SpringBoot项目配置热更新的步骤1.在pom.xml中添加依赖:<dependency><groupId
- 定义: SharedPreferences
- 本文实例讲述了Java数据结构之链表、栈、队列、树的实现方法。分享给大家供大家参考,具体如下:最近无意中翻到一本书,闲来无事写几行代码,实现
- java 中newInstance()方法和new关键字的区别* 它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新
- 介绍记录将elasticsearch集成到spring boot的过程,以及一些简单的应用和helper类使用。接入方式使用spring-b