当前位置: 首页 > news >正文

Spring AOP记录接口访问日志

Spring AOP记录接口访问日志

  • 介绍
  • 应用范围
  • 组成
    • 通知(Advice)
    • 连接点(JoinPoint)
    • 切点(Pointcut)
    • 切面(Aspect)
    • 引入(Introduction)
    • 织入(Weaving)
  • Spring Aop
    • 相关注解
    • 切点表达式
  • 实现接口日志记录
    • 添加日志信息封装类WebLog
    • 添加切面类WebLogAspect
    • 进行接口测试
  • 源码地址

介绍

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

应用范围

后端接口设计时是需要考虑登录认证,除了注册、登录和注销等特殊操作外,其他的请求都需要进行校验是否登录、登录身份认证,普通的方式是每个controller都得重复编写登录认证的接口,若后面进行功能扩展,重复的代码就增加,不利于维护,所以aop就发挥作用了。
除了统⼀的用户登录判断之外,AOP 还可以实现:

  • 统⼀⽇志记录
  • 统⼀⽅法执⾏时间统计
  • 统⼀返回格式
  • 统⼀异常处理
  • 事务开启和提交

也就是说使用 AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP(Object Oriented Programming,⾯向对象编程)的补充和完善。

组成

通知(Advice)

通知描述了切面要完成的工作以及何时执行

  • 前置通知(Before):在目标方法调用前调用通知功能;
  • 后置通知(After):在目标方法调用之后调用通知功能,不关心方法的返回结果;
  • 返回通知(AfterReturning):在目标方法成功执行之后调用通知功能;
  • 异常通知(AfterThrowing):在目标方法抛出异常后调用通知功能;
  • 环绕通知(Around):通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为。

连接点(JoinPoint)

通知功能被应用的时机。比如接口方法被调用的时候就是日志切面的连接点。

切点(Pointcut)

切点定义了通知功能被应用的范围。比如日志切面的应用范围就是所有接口,即所有controller层的接口方法。

切面(Aspect)

切面是通知和切点的结合,定义了何时、何地应用通知功能。

引入(Introduction)

在无需修改现有类的情况下,向现有的类添加新方法或属性。

织入(Weaving)

把切面应用到目标对象并创建新的代理对象的过程。

Spring Aop

相关注解

@Aspect:用于定义切面
@Before:通知方法会在目标方法调用之前执行
@After:通知方法会在目标方法返回或抛出异常后执行
@AfterReturning:通知方法会在目标方法返回后执行
@AfterThrowing:通知方法会在目标方法抛出异常后执行
@Around:通知方法会将目标方法封装起来
@Pointcut:定义切点表达式

切点表达式

指定了通知被应用的范围,表达式格式:

execution(方法修饰符 返回类型 方法所属的包.类名.方法名称(方法参数)
//com.macro.mall.tiny.controller包中所有类的public方法都应用切面里的通知
execution(public * com.macro.mall.tiny.controller.*.*(..))
//com.macro.mall.tiny.service包及其子包下所有类中的所有方法都应用切面里的通知
execution(* com.macro.mall.tiny.service..*.*(..))
//com.macro.mall.tiny.service.PmsBrandService类中的所有方法都应用切面里的通知
execution(* com.macro.mall.tiny.service.PmsBrandService.*(..))

实现接口日志记录

添加日志信息封装类WebLog

用于封装需要记录的日志信息,包括操作的描述、时间、消耗时间、url、请求参数和返回结果等信息。

/*** Controller层的日志封装类*/
public class WebLog {/*** 操作描述*/private String description;/*** 操作用户*/private String username;/*** 操作时间*/private Long startTime;/*** 消耗时间*/private Integer spendTime;/*** 根路径*/private String basePath;/*** URI*/private String uri;/*** URL*/private String url;/*** 请求类型*/private String method;/*** IP地址*/private String ip;/*** 请求参数*/private Object parameter;/*** 请求返回的结果*/private Object result;//省略了getter,setter方法
}

添加切面类WebLogAspect

定义了一个日志切面,在环绕通知中获取日志需要的信息,并应用到controller层中所有的public方法中去。

/*** 统一日志处理切面*/
@Aspect
@Component
@Order(1)
public class WebLogAspect {private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);@Pointcut("execution(public * com.sheep.controller.*.*(..))")public void webLog() {}@Before("webLog()")public void doBefore(JoinPoint joinPoint) throws Throwable {}@AfterReturning(value = "webLog()", returning = "ret")public void doAfterReturning(Object ret) throws Throwable {}@Around("webLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();//获取当前请求对象ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//记录请求信息WebLog webLog = new WebLog();Object result = joinPoint.proceed();Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method.isAnnotationPresent(ApiOperation.class)) {ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);webLog.setDescription(apiOperation.value());}long endTime = System.currentTimeMillis();String urlStr = request.getRequestURL().toString();webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));webLog.setIp(request.getRemoteUser());webLog.setMethod(request.getMethod());webLog.setParameter(getParameter(method, joinPoint.getArgs()));webLog.setResult(result);webLog.setSpendTime((int) (endTime - startTime));webLog.setStartTime(startTime);webLog.setUri(request.getRequestURI());webLog.setUrl(request.getRequestURL().toString());LOGGER.info("{}", JSONUtil.parse(webLog));return result;}/*** 根据方法和传入的参数获取请求参数*/private Object getParameter(Method method, Object[] args) {List<Object> argList = new ArrayList<>();Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {//将RequestBody注解修饰的参数作为请求参数RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);if (requestBody != null) {argList.add(args[i]);}//将RequestParam注解修饰的参数作为请求参数RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);if (requestParam != null) {Map<String, Object> map = new HashMap<>();String key = parameters[i].getName();if (!StringUtils.isEmpty(requestParam.value())) {key = requestParam.value();}map.put(key, args[i]);argList.add(map);}if (requestParam == null && requestBody == null && args != null) {return args;}}if (argList.size() == 0) {return null;} else if (argList.size() == 1) {return argList.get(0);} else {return argList;}}
}

进行接口测试

运行项目并访问:http://localhost:8080/swagger-ui.html

访问下查询接口看看效果
在这里插入图片描述
在这里插入图片描述

源码地址

https://gitee.com/yang1112/learn-project.git

http://www.lryc.cn/news/250702.html

相关文章:

  • 分享89个节日PPT,总有一款适合您
  • PostgreSQL日志中的SQL记录时机 —— log_statement 和 log_min_duration_statement
  • Agent举例与应用
  • CentOS 7 配置tomcat
  • 如何优雅的关闭一个IIS站点
  • 弱网模拟工具
  • Leetcode 第 110 场双周赛 Problem D 2809. 使数组和小于等于 x 的最少时间(DP+贪心+正难则反)
  • 已知数组A[1..n]中元素类型为非负整数,设计算法将其调整为左右两部分,左边所有为奇数,右边所有为偶数,并要求算法的时间复杂度为O(n)
  • ssm+vue的罪犯信息管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。
  • Java/Android 各类型数据构造和各类型数据解析
  • Linux系统---环境变量+内核进程调度队列(选学)
  • Kubernetes 使用插件扩展 kubectl
  • 前端面试题09
  • 网站更换IP的四大注意事项
  • 策略模式与简单工厂模式:终结if-else混乱,让代码更清爽
  • TCP三次握手过程
  • 04-配置远程仓库的SSH免密登陆
  • 【中文编码】利用bert-base-chinese中的Tokenizer实现中文编码嵌入
  • 一文解决msxml3.dll文件缺失问题,快速修复msxml3.dll
  • 《React 知识点》第一篇 大括号使用{}
  • kafka入门(二): 位移提交
  • PG时间计算
  • 基于51单片机的交通灯_可调时间_夜间+紧急模式
  • 网络通信原理,进制转化总结
  • 西南科技大学(数据结构A)期末自测练习三
  • 【halcon】裁剪
  • vue+less+style-resources-loader 配置全局颜色变量
  • 第二次量子化
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • 【代码】多种调度模式下的光储电站经济性最优 储能容量配置分析matlab/yalmip