SpringMVC_day02:5.拦截器

5,拦截器

对于拦截器这节的知识,我们需要学习如下内容:

  • 拦截器概念

  • 入门案例

  • 拦截器参数

  • 拦截器工作流程分析

5.1 拦截器概念

讲解拦截器的概念之前,我们先看一张图:

(1)浏览器发送一个请求会先到Tomcat的web服务器

(2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源

(3)如果是静态资源,会直接到Tomcat的项目部署目录下去直接访问

(4)如果是动态资源,就需要交给项目的后台代码进行处理

(5)在找到具体的方法之前,我们可以去配置过滤器(可以配置多个),按照顺序进行执行

(6)然后进入到到中央处理器(SpringMVC中的内容),SpringMVC会根据配置的规则进行拦截

(7)如果满足规则,则进行处理,找到其对应的controller类中的方法进行执行,完成后返回结果

(8)如果不满足规则,则不进行处理

(9)这个时候,如果我们需要在每个Controller方法执行的前后添加业务,具体该如何来实现?

这个就是拦截器要做的事。

  • 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行

  • 作用:

    • 在指定的方法调用前后执行预先设定的代码

    • 阻止原始方法的执行

    • 总结:拦截器就是用来做增强

看完以后,大家会发现

  • 拦截器和过滤器在作用和执行顺序上也很相似

所以这个时候,就有一个问题需要思考:拦截器和过滤器之间的区别是什么?

  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术

  • 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

🧠 理论理解
拦截器是 SpringMVC 的核心扩展点,允许在请求到达 Controller 前后、处理完成后,插入横切逻辑(cross-cutting concern)。它基于 Java 的回调机制和责任链模式,实现按顺序“拦截”请求,决定是否放行、是否追加行为。
与过滤器(Filter)相比,拦截器更专注于 SpringMVC 内部执行链,只作用于 Spring 管理的请求,更易集成 Spring 上下文、获取 Controller 方法等细节。

🏢 企业实战理解
在大厂,拦截器是后台系统、微服务架构的标配组件。

  • 字节跳动后台项目:常用来做用户身份验证(如登录态、Token 校验)、埋点日志、A/B 实验分流。

  • Google Cloud Console:利用拦截器动态注入 trace-id,实现分布式链路追踪。

  • 阿里云 API 网关:拦截器配合网关实现动态路由、限流、降级、黑白名单。
    这些场景都需要精细化控制请求链,而不仅是 Servlet 层粗粒度过滤。

 

面试题 1

拦截器和过滤器有什么区别?你在实际项目中怎么选择?

答案:

  • 拦截器(Interceptor):SpringMVC 层提供,拦截的是进入 DispatcherServlet 的请求,能拿到 Spring 上下文、Controller 方法、注解信息。

  • 过滤器(Filter):Servlet 规范提供,拦截所有请求(包括静态资源、非 Spring 管理的请求),独立于 Spring 存在。

选择:

  • 与 SpringMVC 紧密相关的(如登录校验、权限检查、业务拦截)用拦截器。

  • 与整个 Web 容器相关的(如编码、跨域、全局防御、日志埋点、请求修饰)用过滤器。

在字节、阿里项目里,两者通常配合使用,过滤器在外层做全局策略,拦截器在内层做业务策略。

场景题 1

字节跳动后台系统有多级拦截器链:登录校验拦截器、权限拦截器、埋点日志拦截器。你发现线上日志显示部分请求没有经过埋点拦截器,只有权限校验日志,为什么?你怎么排查?

答案:
首先分析:拦截器是责任链模型,任何一个 preHandle 返回 false,后续链条(包括其他拦截器、Controller 方法)都不会执行。
排查步骤:
1️⃣ 检查日志发现权限拦截器 preHandle 在某些请求上返回了 false(比如因为用户无权限)。
2️⃣ 因为权限拦截器排在埋点拦截器之前,直接中断了链条。
解决方案:

  • 调整拦截器顺序,让埋点拦截器最先执行(记录全量请求)。

  • 即使 preHandle 返回 false,在 afterCompletion 中补充日志埋点(确保不中断链路追踪)。
    字节内部在埋点系统中,通常会优先挂载 trace 拦截器,确保所有请求都能生成 trace-id。

 

场景题 2

你在阿里云工作,某个团队在拦截器 preHandle 里做了数据库查询,线上突发流量导致数据库压力飙升。请问为什么设计有问题?怎么优化?

答案:
问题分析:拦截器 preHandle 属于请求同步链路,是高频执行点。做重逻辑(特别是数据库 IO、远程调用)会直接拖慢 QPS。
优化方案:

  • 尽量把数据库查询提前放到网关层或缓存层(比如 Redis 缓存权限信息)。

  • 必须查数据库的场景,用批量预加载、延迟加载、异步方式。

  • 引入分布式限流、熔断机制,避免请求洪峰打爆核心链路。
    阿里云内部的微服务平台通常用 Sentinel、Hystrix 做保护,拦截器里只走缓存或上下文数据。

5.2 拦截器入门案例

5.2.1 环境准备
  • 创建一个Web的Maven项目

  • pom.xml添加SSM整合所需jar包

    <?xml version="1.0" encoding="UTF-8"?>
    ​
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    ​
      <groupId>com.itheima</groupId>
      <artifactId>springmvc_12_interceptor</artifactId>
      <version>1.0-SNAPSHOT</version>
      <packaging>war</packaging>
    ​
      <dependencies>
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.0</version>
        </dependency>
      </dependencies>
    ​
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.1</version>
            <configuration>
              <port>80</port>
              <path>/</path>
            </configuration>
          </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
      </build>
    </project>
  • 创建对应的配置类

    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
        protected Class<?>[] getRootConfigClasses() {
            return new Class[0];
        }
    ​
        protected Class<?>[] getServletConfigClasses() {
            return new Class[]{SpringMvcConfig.class};
        }
    ​
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    ​
        //乱码处理
        @Override
        protected Filter[] getServletFilters() {
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            return new Filter[]{filter};
        }
    }
    ​
    @Configuration
    @ComponentScan({"com.itheima.controller"})
    @EnableWebMvc
    public class SpringMvcConfig{
       
    }

  • 创建模型类Book

    public class Book {
        private String name;
        private double price;
    ​
        public String getName() {
            return name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    ​
        public double getPrice() {
            return price;
        }
    ​
        public void setPrice(double price) {
            this.price = price;
        }
    ​
        @Override
        public String toString() {
            return "Book{" +
                    "书名='" + name + '\'' +
                    ", 价格=" + price +
                    '}';
        }
    }

  • 编写Controller

    @RestController
    @RequestMapping("/books")
    public class BookController {
    ​
        @PostMapping
        public String save(@RequestBody Book book){
            System.out.println("book save..." + book);
            return "{'module':'book save'}";
        }
    ​
        @DeleteMapping("/{id}")
        public String delete(@PathVariable Integer id){
            System.out.println("book delete..." + id);
            return "{'module':'book delete'}";
        }
    ​
        @PutMapping
        public String update(@RequestBody Book book){
            System.out.println("book update..."+book);
            return "{'module':'book update'}";
        }
    ​
        @GetMapping("/{id}")
        public String getById(@PathVariable Integer id){
            System.out.println("book getById..."+id);
            return "{'module':'book getById'}";
        }
    ​
        @GetMapping
        public String getAll(){
            System.out.println("book getAll...");
            return "{'module':'book getAll'}";
        }
    }

最终创建好的项目结构如下:

面试题 2

SpringMVC 拦截器的三个方法(preHandle、postHandle、afterCompletion)有什么区别?分别适合做哪些事情?

答案:

  • preHandle:Controller 方法执行前调用,决定请求是否放行。适合做:登录校验、权限校验、参数校验、流量控制。

  • postHandle:Controller 方法执行后调用(但视图渲染前)。适合做:修改视图模型、追加响应数据(主要对视图有效,对纯 JSON 意义不大)。

  • afterCompletion:请求完全结束后调用(无论是否异常)。适合做:资源清理、异常日志记录、性能统计。

在大厂实践中,preHandle 是最常用的钩子,afterCompletion 用于保证无论成功失败都能收集指标或输出审计日志。

 

场景题 3

Google Cloud 后台系统支持多租户,每个租户有不同的路由和访问权限。你如何用拦截器实现多租户上下文注入?

答案:
设计方案:

  • preHandle 中解析请求头或 Token,提取租户 ID(tenantId)。

  • tenantId 写入线程上下文(如 ThreadLocal、RequestContext)。

  • 后续业务层、数据库层(如 MyBatis、多租户数据源)可以直接读取当前上下文。
    注意事项:

  • 确保 afterCompletion 中清理 ThreadLocal,防止线程池复用带来的脏数据。

  • 对跨服务请求,租户上下文需透传 trace(结合分布式 tracing)。
    Google Cloud Platform 内部有专门的 Context 管理模块和拦截器挂钩,确保租户、请求上下文贯穿调用链。

5.2.2 拦截器开发
步骤1:创建拦截器类

让类实现HandlerInterceptor接口,重写接口中的三个方法。

@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    //原始方法调用前执行的内容
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");
        return true;
    }
​
    @Override
    //原始方法调用后执行的内容
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }
​
    @Override
    //原始方法调用完成后执行的内容
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

注意:拦截器类要被SpringMVC容器扫描到。

步骤2:配置拦截器类
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;
​
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }
​
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        //配置拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books" );
    }
}
步骤3:SpringMVC添加SpringMvcSupport包扫描
@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig{
   
}
步骤4:运行程序测试

使用PostMan发送http://localhost/books

如果发送http://localhost/books/100会发现拦截器没有被执行,原因是拦截器的addPathPatterns方法配置的拦截路径是/books,我们现在发送的是/books/100,所以没有匹配上,因此没有拦截,拦截器就不会执行。

步骤5:修改拦截器拦截规则
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;
​
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }
​
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        //配置拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*" );
    }
}

这个时候,如果再次访问http://localhost/books/100,拦截器就会被执行。

最后说一件事,就是拦截器中的preHandler方法,如果返回true,则代表放行,会执行原始Controller类中要请求的方法,如果返回false,则代表拦截,后面的就不会再执行了。

步骤6:简化SpringMvcSupport的编写
@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

此后咱们就不用再写SpringMvcSupport类了。

最后我们来看下拦截器的执行流程:

当有拦截器后,请求会先进入preHandle方法,

如果方法返回true,则放行继续执行后面的handle[controller的方法]和后面的方法

如果返回false,则直接跳过后面方法的执行。

🧠 理论理解
拦截器通过实现 HandlerInterceptor 接口(或使用 HandlerInterceptorAdapter)定义三类方法:

  • preHandle:控制是否放行。

  • postHandle:业务执行后,调整响应数据。

  • afterCompletion:请求完成后收尾工作。
    配置方式有两种:通过 WebMvcConfigurerWebMvcConfigurationSupport,前者更灵活。

🏢 企业实战理解
在大厂项目中,拦截器不仅是开发用的,还会被运维和平台层“挂钩”。

  • 字节的 RPC 框架有统一链路拦截器,用于埋点分析、性能监控。

  • 阿里云的多租户系统,用拦截器为每个租户动态注入上下文(如租户 ID、权限信息)。

  • Google、OpenAI 的 API 系统,用拦截器统一实现跨 API 的安全验证、流控、熔断。
    这些功能常常比单个业务模块更关键,影响整体系统的可观测性和稳定性。

 

面试题 3

如果系统配置了多个拦截器,它们的执行顺序是怎样的?能举一个实际应用场景吗?

答案:

  • preHandle 按配置顺序执行(先进先出)。

  • postHandleafterCompletion 按配置顺序逆序执行(后进先出)。

实际场景:

  • 拦截器 1:Trace 拦截器(埋点、日志、链路追踪)。

  • 拦截器 2:权限拦截器(登录、角色、菜单权限)。

  • 拦截器 3:接口幂等性拦截器(防重复提交)。
    这三者需要按顺序串联起来保证链路上下文、业务校验和接口安全。阿里、字节的后台系统中,这种多层拦截是常规配置。

场景题 4

你在 OpenAI 团队开发 API 平台,客户要求每次调用 API 后要有操作审计记录,包括:谁调用、调用了什么、成功还是失败。你怎么用拦截器实现?

答案:
方案设计:

  • preHandle 中记录:调用方身份(从 Header、Token 中解析)、请求 URL、入参摘要。

  • postHandleafterCompletion 中记录:请求结果、状态码、异常信息(如果有)。

  • 将审计数据异步写入日志系统或审计数据库(避免阻塞主线程)。
    OpenAI 这类高 QPS 系统会用专门的异步消息队列(如 Kafka)接收审计日志,后端用批处理或流式处理系统(如 Flink)分析审计数据。

 

5.3 拦截器参数

5.3.1 前置处理方法

原始方法之前运行preHandle

public boolean preHandle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object handler) throws Exception {
    System.out.println("preHandle");
    return true;
}
  • request:请求对象

  • response:响应对象

  • handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装

使用request对象可以获取请求数据中的内容,如获取请求头的Content-Type

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String contentType = request.getHeader("Content-Type");
    System.out.println("preHandle..."+contentType);
    return true;
}

使用handler参数,可以获取方法的相关信息

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod hm = (HandlerMethod)handler;
    String methodName = hm.getMethod().getName();//可以获取方法的名称
    System.out.println("preHandle..."+methodName);
    return true;
}
5.3.2 后置处理方法

原始方法运行后运行,如果原始方法被拦截,则不执行

public void postHandle(HttpServletRequest request,
                       HttpServletResponse response,
                       Object handler,
                       ModelAndView modelAndView) throws Exception {
    System.out.println("postHandle");
}

前三个参数和上面的是一致的。

modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整

因为咱们现在都是返回json数据,所以该参数的使用率不高。

5.3.3 完成处理方法

拦截器最后执行的方法,无论原始方法是否执行

public void afterCompletion(HttpServletRequest request,
                            HttpServletResponse response,
                            Object handler,
                            Exception ex) throws Exception {
    System.out.println("afterCompletion");
}

前三个参数与上面的是一致的。

ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理

因为我们现在已经有全局异常处理器类,所以该参数的使用率也不高。

这三个方法中,最常用的是==preHandle==,在这个方法中可以通过返回值来决定是否要进行放行,我们可以把业务逻辑放在该方法中,如果满足业务则返回true放行,不满足则返回false拦截。

🧠 理论理解

  • preHandle:拿到 HttpServletRequestHttpServletResponse,可以提前解析请求头、参数、URL。

  • handler:通常是 HandlerMethod,能解析出目标方法、注解、类信息。

  • postHandle:能读取 ModelAndView,调整返回的视图或数据(对 JSON 场景意义不大)。

  • afterCompletion:即使出错也执行,用于收尾、清理、记录异常。

🏢 企业实战理解
在大厂场景中:

  • 字节跳动会在 preHandle 中提取 trace-id,贯穿到所有日志、埋点。

  • Google 的 API 系统,在 preHandle 中做请求签名校验,防止重放攻击。

  • NVIDIA 的 GPU 云平台,用 afterCompletion 写入审计日志,保证操作记录完整性。
    这些参数不仅是技术细节,更是支撑系统运维、数据安全、性能优化的基石。

 

面试题 4

在高并发场景下,拦截器设计要注意哪些问题?

答案:

  • 不要在拦截器中存储请求相关状态到全局变量(因为拦截器是单例)。

  • 避免在拦截器中执行重逻辑、重 IO 操作(如写 DB、远程调用),应尽量异步或用消息队列。

  • 保证幂等性,防止多线程场景下拦截器引入脏数据。

  • 可结合分布式锁、限流、断路器(如 Sentinel、Hystrix)保护核心服务。

字节、阿里等大厂的微服务通常用网关层的拦截器实现限流、熔断,单体项目则结合本地缓存、异步化等优化性能。

场景题 5

你在腾讯负责后台系统,安全部门要求所有 POST、PUT、DELETE 接口都必须有 CSRF 防御机制。你如何基于 SpringMVC 拦截器实现?

答案:
实现步骤:
1️⃣ 在 preHandle 中判断请求方法是否为 POST、PUT、DELETE。
2️⃣ 检查请求头或请求参数中是否携带合法的 CSRF Token。
3️⃣ 如果没有或不合法,直接返回错误响应(HTTP 403 Forbidden)。
4️⃣ 如果合法,放行进入后续业务逻辑。
注意:这种安全拦截器通常在网关或统一安全模块里统一配置,避免每个微服务单独实现。

 

5.4 拦截器链配置

目前,我们在项目中只添加了一个拦截器,如果有多个,该如何配置?配置多个后,执行顺序是什么?

5.4.1 配置多个拦截器
步骤1:创建拦截器类

实现接口,并重写接口中的方法

@Component
public class ProjectInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...222");
        return false;
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...222");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...222");
    }
}
步骤2:配置拦截器类
@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    @Autowired
    private ProjectInterceptor2 projectInterceptor2;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
        registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
    }
}

步骤3:运行程序,观察顺序

拦截器执行的顺序是和配置顺序有关。就和前面所提到的运维人员进入机房的案例,先进后出。

  • 当配置多个拦截器时,形成拦截器链

  • 拦截器链的运行顺序参照拦截器添加顺序为准

  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行

  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作

 

preHandle:与配置顺序相同,必定运行

postHandle:与配置顺序相反,可能不运行

afterCompletion:与配置顺序相反,可能不运行。

这个顺序不太好记,最终只需要把握住一个原则即可:==以最终的运行结果为准==

🧠 理论理解
当系统中配置多个拦截器,它们按注册顺序形成链式结构:

  • preHandle 按顺序执行(先进先出)。

  • postHandleafterCompletion 按逆序执行(后进先出)。
    这本质是责任链模式,允许多个拦截器独立负责自己的逻辑,但要注意链中任意一个返回 false,后续链条都不再继续。

🏢 企业实战理解
在大厂中,拦截器链常被用来分层:

  • 第一级:全局级,比如 trace、日志、异常处理。

  • 第二级:业务模块级,比如权限、流控、灰度策略。

  • 第三级:接口专属,比如参数转换、动态注入。
    字节、阿里、Google 都有专门的“拦截器平台”或“中间件”,帮助统一配置和管理这些链路,甚至支持动态启停、在线调优,保证系统稳定可靠。

 

面试题 5

拦截器中获取到 HandlerMethod 后,能做什么高级用法?

答案:
通过 HandlerMethod 可以:

  • 获取 Controller 类、方法、方法参数。

  • 读取方法或类上的注解,比如自定义 @RequiresPermission@AuditLog

  • 动态决定是否拦截或放行(基于注解、反射信息)。

企业实践中,很多基于注解的权限拦截、埋点统计、灰度策略、A/B 测试分流,都是通过 HandlerMethod 动态解析注解和方法元信息实现的。

面试题 6

你如何设计一个全局异常处理 + 拦截器配合的方案,保证系统健壮性?

答案:

  • 拦截器层:只做请求前后的预处理、埋点、数据记录,不处理异常。

  • 全局异常处理器(如 @ControllerAdvice):捕获所有未被处理的异常,返回统一错误响应,记录日志、报警。

  • afterCompletion:用于记录请求级别的异常、耗时、请求上下文信息,补充全局日志链。

在阿里、字节、Google 这些大厂,通常采用分层异常处理架构,拦截器专注埋点,核心异常处理由框架统一接管,并与监控系统(如 Sentry、Prometheus)联动。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值