SpringBoot 统计API接口用时该使用过滤器还是拦截器?

统计请求的处理时间(用时)既可以使用 Servlet 过滤器(Filter),也可以使用 Spring 拦截器(Interceptor)。两者都可以在请求处理前后插入自定义逻辑,从而实现对请求响应时间的统计。

使用建议

如果你需要在更底层、与框架无关的地方记录所有请求(包括静态资源请求)的处理时间,那么 Servlet 过滤器是一个更好的选择。

如果你正在使用 Spring MVC 并且关注的是 Controller 层的处理时间,或者需要访问到 Spring 上下文中的服务,那么 Spring 拦截器可能更为合适。

代码样例

Servlet 过滤器(Filter)

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.time.Instant;// @Component 注册时会new 这里无需指定 registration.setFilter(new LogFilter());
@Slf4j
public class LogFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.err.println("***LogFilter.doFilter.start***");HttpServletRequest httpReq = (HttpServletRequest) request;long startTime = Instant.now().toEpochMilli();// 记录请求开始时间及请求信息log.warn("LogFilter.doFilter: Start processing request at {} - {}", Instant.now(), httpReq.getRequestURI());try {// 将请求传递给下一个过滤器或目标资源chain.doFilter(request, response);} finally {// 记录请求结束时间及响应状态码long endTime = Instant.now().toEpochMilli();int statusCode = ((HttpServletResponse) response).getStatus();log.warn("LogFilter.doFilter: Finished processing request at {} - {} in {} ms. Status code: {}", Instant.now(), httpReq.getRequestURI(), (endTime - startTime), statusCode);}System.err.println("***LogFilter.doFilter.end***");}
}

注册过滤器(Filter)

@Configuration
public class AppConfig {@Beanpublic FilterRegistrationBean<LogFilter> tokenFilterRegistration() {FilterRegistrationBean<LogFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new LogFilter());// 可以设置过滤器名称registration.setName("logFilter");// 设置拦截规则registration.addUrlPatterns("/*"); // 拦截所有请求// 设置过滤器执行顺序,默认为0,数值越小优先级越高registration.setOrder(1);return registration;}
}

Spring 拦截器(Interceptor)

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import java.time.Instant;@Component
@Slf4j
public class LogInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {System.err.println("***LogInterceptor.preHandle***");long startTime = Instant.now().toEpochMilli();request.setAttribute("startTime", startTime);log.warn("LogInterceptor.postHandle: Start processing request at {} - {}", Instant.now(), request.getRequestURI());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {System.err.println("***LogInterceptor.preHandle***");// 获取请求开始时间Long startTime = (Long) request.getAttribute("startTime");if (startTime != null) {long executionTime = Instant.now().toEpochMilli() - startTime;int statusCode = response.getStatus();log.warn("LogInterceptor.postHandle: Finished processing request at {} - {} in {} ms. Status code: {}", Instant.now(), request.getRequestURI(), executionTime, statusCode);}}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.err.println("***LogInterceptor.afterCompletion***");// 在此可以添加额外的后处理逻辑,但本例中我们不需要}
}

注册拦截器(Interceptor)

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate ResponsePostInterceptor responsePostInterceptor;@Autowiredprivate LogInterceptor logInterceptor;/*** 为拦截器注册表添加拦截器** @param registry 拦截器注册表*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 在Spring MVC配置中注册一个名为responsePostInterceptor的拦截器,使其能够对匹配路径“/**”(即对应用程序中的所有路径)的请求进行拦截registry.addInterceptor(responsePostInterceptor).addPathPatterns("/**");registry.addInterceptor(logInterceptor).addPathPatterns("/**");}
}

[Ref] 在Spring Boot中注册过滤器几种方式

测试验证

在这里插入图片描述

在这里插入图片描述

# 过滤器开始计时
***LogFilter.doFilter.start***
[2024-01-17 08:17:55] [WARN ] [http-nio-8080-exec-2] [LogFilter.java:22][LogFilter.doFilter: Start processing request at 2024-01-17T00:17:55.662652400Z - /students]
***RequestHeaderCheckFilter.doFilter.start***# 拦截器组的 preHandle
***ResponsePostInterceptor.preHandle***
# log用时拦截器开始计时
***LogInterceptor.preHandle***
[2024-01-17 08:17:55] [WARN ] [http-nio-8080-exec-2] [LogInterceptor.java:20][LogInterceptor.postHandle: Start processing request at 2024-01-17T00:17:55.852229500Z - /students]# Controller层
***StudentController.edit***
[2024-01-17 08:17:56] [INFO ] [http-nio-8080-exec-2] [HikariDataSource.java:110][practisedb - Starting...]
[2024-01-17 08:17:56] [INFO ] [http-nio-8080-exec-2] [HikariPool.java:565][practisedb - Added connection com.mysql.cj.jdbc.ConnectionImpl@34a6ebfc]
[2024-01-17 08:17:56] [INFO ] [http-nio-8080-exec-2] [HikariDataSource.java:123][practisedb - Start completed.]# @ControllerAdvice对Response增强,比如修改状态码,补充header值
***ResponsePostAdvice.supports***
***ResponsePostAdvice.beforeBodyWrite***# 拦截器组的 postHandle
***LogInterceptor.postHandle***
# log用时拦截器结束计时
[2024-01-17 08:17:56] [WARN ] [http-nio-8080-exec-2] [LogInterceptor.java:32][LogInterceptor.postHandle: Finished processing request at 2024-01-17T00:17:56.636557900Z - /students in 784 ms. Status code: 200]
***ResponsePostInterceptor.postHandle***# 拦截器组的 afterCompletion
***LogInterceptor.afterCompletion***
***ResponsePostInterceptor.afterCompletion***# 过滤器结束计时
***RequestHeaderCheckFilter.doFilter.end***
[2024-01-17 08:17:56] [WARN ] [http-nio-8080-exec-2] [LogFilter.java:31][LogFilter.doFilter: Finished processing request at 2024-01-17T00:17:56.920165800Z - /students in 1258 ms. Status code: 200]
***LogFilter.doFilter.end***

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

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

相关文章

信创运维:数字化转型的关键支撑

随着国家信息化建设的不断深入&#xff0c;信息技术创新&#xff08;信创&#xff09;已成为推动我国经济发展的重要动力。在这样一个大环境下&#xff0c;信创运维作为保障信创环境稳定、高效运行的关键环节&#xff0c;其重要性日益凸显。本文将探讨信创运维的发展背景、数字…

【前端】vue.js从入门到项目实战笔记

文章目录 第三章3.1 插值绑定&#xff08;{{}}&#xff0c; v-html&#xff09;3.1.1 文本插值3.1.2 HTML插值 3.2 属性绑定 v-bind3.2.1 指令v-bind3.2.3 类名和样式绑定 【前端目录贴】 第三章 3.1 插值绑定&#xff08;{{}}&#xff0c; v-html&#xff09; 文本插值中的代…

RestTemplate调用Http方法

场景&#xff1a;前端调用后端的接口完成设备参数的更新&#xff0c;后端在接口代码中需要调用设备端提供的接口来完成设备参数的更新。 RestTemplate 是用于同步client端访问 Restful 服务的一个核心类 默认使用 JDK 提供的包去建立HTTP连接 为每种 HTTP 请求都实现了相关的…

vue3前端开发,感受一下组合式api和VUE2选项式的差异

vue3前端开发,感受一下组合式api和VUE2选项式的差异&#xff01;今天开始&#xff0c;正式开始&#xff0c;进入学习Vue3的内容。以后代码&#xff0c;案例分享&#xff0c;都会采用组合式api的模式为大家做展示。 今天是第一节&#xff0c;带大家感受一下&#xff0c;Vue3的组…

css-盒子等样式学习

盒子居中&#xff0c;继承外层盒子的宽高 兼容性&#xff08;border-box&#xff09;将边框收到盒子内部 初始化div 不用管box-setting content-box 还原 创建为一个类 &#xff0c;让所有需要还原的类 进行继承 padding 用法表示margin上下左右边距 body 外边距&…

Redis 配置

Redis 的配置文件位于 Redis 安装目录下&#xff0c;文件名为 redis.conf。 你可以通过 CONFIG 命令查看或设置配置项。 语法 Redis CONFIG 命令格式如下&#xff1a; redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME 实例 redis 127.0.0.1:6379> CONFIG GET l…

线程和进程的区别(从JVM角度出发)

进程与线程的区别 线程具有许多传统进程所具有的特征&#xff0c;故又称为轻型进程(Light—Weight Process)或进程元&#xff1b;而把传统的进程称为重型进程(Heavy—Weight Process)&#xff0c;它相当于只有一个线程的任务。在引入了线程的操作系统中&#xff0c;通常一个进…

linux-nfc neard 编译、安装与运行

项目github地址&#xff1a; https://github.com/linux-nfc/neard git clone地址&#xff1a; https://github.com/linux-nfc/neard.git 1.安装依赖库 clone完源码切换到目录neard里。这个项目需要依赖一下库&#xff1a; - GCC compiler - D-Bus library - GLib library …

CHAPTER 9: 《DESIGN A WEB CRAWLER》第9章 《设计一个web爬虫》

CHAPTER 9: 《DESIGN A WEB CRAWLER》第九章 设计一个web爬虫 在本章中&#xff0c;我们将重点介绍网络爬虫设计&#xff1a;一种有趣而经典的系统设计 面试问题。 网络爬虫被称为机器人或蜘蛛。它被搜索引擎广泛用于发现网络上的新内容或更新内容。内容可以是网页、图像、视频…

66.Go从零搭建一个orm框架【简版】

文章目录 一&#xff1a;前置学习1、 为什么要用orm2、Golang里面是如何原生连接MySQL的3、ORM框架构想 二: 开始造1、连接Connect2、设置/读取表名Table/GetTable3、新增/替换Insert/Replace4、条件Where5、条件OrWhere6、删除Delete7、修改Update8、查询9、设置查询字段Field…

使用python将图像复刻到excel表格

上班摸鱼的时候突然想到以前有个日本老爷爷用excel表格作画感觉挺牛的&#xff0c; 然后想到图像可以分割成一个个小块&#xff0c;excel表格也是一个个小格子&#xff0c;将小块的坐标和颜色对应填充到表格中&#xff0c;不就行了。 效果如下&#xff1a; 完整代码&#xff1…

electron+vite+vue3 快速入门教程

文章目录 前言一、electron是什么&#xff1f;二、electron 进程模型1.主进程2.渲染进程3.预加载脚本4.进程通信4.1 sendon&#xff08;单向&#xff09;4.2 invokehandle (双向)4.3 主进程向渲染进程发送事件 三、窗口创建与应用事件四、技术栈和构建工具五、electron-vite安装…

如何实现指定列值排序? ------ MySQL中的field()函数 [让排序更简单]

想自定义排序规则就用field&#xff08;&#xff09; filed(“列名”&#xff0c;“值1”,“值2”…) 案例&#xff1a;要求 STATUS 列 按 N&#xff0c;Y&#xff0c;E&#xff0c; 排序。 select * from 表名 ORDER BY field(STATUS,N,"Y","E") 效果…

jQuery —— W3school 详解 简单易懂 (一)

jQuery 是一个 JavaScript 函数库。jQuery 极大地简化了 JavaScript 编程。jQuery 很容易学习。 <html> <head> <script type"text/javascript" src"jquery.js"></script> <script type"text/javascript"> $(doc…

android List,Set,Map区别和介绍

List 元素存放有序&#xff0c;元素可重复 1.LinkedList 链表&#xff0c;插入删除&#xff0c;非线性安全&#xff0c;插入和删除操作是双向链表操作&#xff0c;增加删除快&#xff0c;查找慢 add(E e)//添加元素 addFirst(E e)//向集合头部添加元素 addList(E e)//向集合…

【Python 千题 —— 基础篇】不吉利的数字

题目描述 题目描述 在西方,“13”被称为不吉利的数字,这是因为耶稣与13个弟子共进晚餐时耶稣的第13个弟子出卖了耶稣,且耶稣受难的日期是13日。所以西方的门牌号会跳过13号,假设这栋楼有16户,请为这栋楼的每一户设立门牌号。 输入描述 无 输出描述 依次输出这栋楼每…

C++核心编程之通过类和对象的思想对文件进行操作

目录 ​​​​​​​一、文件操作 1. 文件类型分类&#xff1a; 2. 操作文件的三大类 二、文本文件 1.写文件 2.读文件 三、二进制文件 1.写二进制文件 2.读二进制文件 一、文件操作 程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放 通过文件可以将…

Spring 中 HttpServletRequest 作为成员变量是安全的吗?

在使用spring框架开发的时候&#xff0c;经常会在controller类中看到 HttpServletRequest 对象参数&#xff0c;一般我们都是直接使用&#xff0c;但是它是何时、怎么注入到 spring 容器的呢 &#xff1f;另外以成员变量注入的 request 是线程安全的吗 ? Controller public c…

react umi/max 封装页签组件

1. models/tabs // 全局共享数据示例 import { useState } from react;const useUser () > {const [items, setItems] useState<any[]>([]); // 页签的全局Item数据const [key, setKey] useState<string>(/home); // 页签的高亮Keyreturn {items,setItems…

【协议】HTTP、HTTPS和HTTP2.0学习总结

1. TCP/IP四层协议 记得大学学网络课程的时候&#xff0c;学的都是OSI/RM七层协议&#xff0c;应用层 -> 表示层 -> 会话层 -> 传输层->网络层->数据链路层->物理层&#xff0c;当时学的时候&#xff0c;感觉太抽象了&#xff0c;学得个一知半解。大脑在接收…