详细分析SpringMvc中HandlerInterceptor拦截器的基本知识(附Demo)

news2024/11/18 6:39:14

目录

  • 前言
  • 1. 基本知识
  • 2. Demo
  • 3. 实战解析

前言

对于Java的基本知识推荐阅读:

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)

1. 基本知识

HandlerInterceptor 是 Spring MVC 提供的一种机制,用来在请求处理的不同阶段进行拦截

它通常用于日志记录、权限校验、性能监控等操作
继承 HandlerInterceptor 允许定义自定义的逻辑,在请求被处理之前、之后以及完成后执行额外的任务

HandlerInterceptor 接口中包含三个主要方法

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    在请求处理之前调用(即在控制器方法调用之前)
    返回 boolean 类型,如果返回 true,则继续处理请求;如果返回 false,则中断请求

  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    在请求被处理之后、视图被渲染之前调用
    用于对 ModelAndView 做进一步的修改或处理

  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    在整个请求完成后(包括视图渲染完成后)调用
    用于清理资源、记录日志或执行后续的逻辑

HandlerInterceptor 使用场景

  1. 日志记录:拦截请求并记录请求信息,如 URL、参数、处理时间等
  2. 权限校验:在进入控制器方法之前,检查用户是否有权限访问某个资源
  3. 性能监控:统计每个请求的耗时,以便优化性能
  4. 会话管理:检查用户会话状态,判断用户是否已经登录

基本的 API 介绍

  • HttpServletRequest:表示客户端的请求,可以从中获取请求的 URL、参数、请求体等
  • HttpServletResponse:表示服务器端的响应,可以向客户端写入响应数据
  • Handler:处理器,通常是控制器方法或其代理对象(通常是 HandlerMethod)
  • ModelAndView:封装了数据模型和视图信息,可以在 postHandle 方法中对其进行修改

使用 HandlerInterceptor 的步骤

  1. 创建拦截器类:实现 HandlerInterceptor 接口并重写其方法
  2. 注册拦截器:在 Spring 配置中,将自定义的拦截器注册到拦截器链中
  3. 配置拦截路径:可以选择对哪些 URL 进行拦截,或者排除哪些 URL

2. Demo

在这里插入图片描述

  1. 创建一个自定义拦截器类
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor {

    // 在控制器处理请求之前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle: 请求URL -> " + request.getRequestURI());
        // 这里可以进行权限校验、记录日志等操作
        // 如果返回 false,请求会被终止
        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: 请求完成,清理资源");
    }
}
  1. 注册拦截器

需要将这个拦截器注册到 Spring MVC 中

通常,在配置类中添加拦截器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器,并指定拦截路径
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**") // 拦截所有路径
                .excludePathPatterns("/login", "/register"); // 排除某些路径
    }
}
  1. 创建简单的测试控制器

为了测试拦截器的效果,可以创建一个简单的控制器:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/test")
    public String test() {
        return "拦截器测试成功!";
    }

    @GetMapping("/login")
    public String login() {
        return "这是登录页面,不会被拦截!";
    }
}
  1. 运行程序并测试

启动 Spring Boot 应用后,访问 /test URL

在这里插入图片描述

将会在控制台看到以下输出:

preHandle: 请求URL -> /test
postHandle: 请求处理完毕
afterCompletion: 请求完成,清理资源

在这里插入图片描述

如果访问 /login,因为在 WebConfig 中排除了 /login 路径,拦截器不会生效

在这里插入图片描述

总的来说

HandlerInterceptor 是 Spring MVC 中非常有用的拦截器机制,它可以在请求处理的各个阶段执行自定义逻辑

  • 常用的拦截器功能包括日志记录、权限检查、性能监控等
  • 使用步骤:定义拦截器类、注册拦截器、配置拦截路径

3. 实战解析

@Slf4j // 引入 Lombok 提供的注解,用于简化日志的使用,不需要显式定义 logger 对象。
public class ApiAccessLogInterceptor implements HandlerInterceptor {

    // 定义一个常量,用于存储处理方法(HandlerMethod)的属性名称,这个属性将在后续的过滤器中使用。
    public static final String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD";

    // 定义一个常量,用于存储 StopWatch 对象的属性名称,StopWatch 是用于记录请求耗时的计时器。
    private static final String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch";

    /**
     * 拦截器的前置处理方法 (preHandle),在请求处理前调用。
     * 
     * @param request  当前的 HttpServletRequest 对象,包含了请求信息。
     * @param response 当前的 HttpServletResponse 对象,包含了响应信息。
     * @param handler  请求处理器,通常是一个 Controller 中的方法,或其对应的代理对象。
     * @return 如果返回 true,则继续执行后续的拦截器或目标方法;如果返回 false,则中断请求流程。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 记录 HandlerMethod(处理方法),将其存储到 request 属性中,以供后续的过滤器或日志使用。
        HandlerMethod handlerMethod = handler instanceof HandlerMethod ? (HandlerMethod) handler : null;
        if (handlerMethod != null) {
            request.setAttribute(ATTRIBUTE_HANDLER_METHOD, handlerMethod);
        }

        // 打印请求日志。如果当前环境不是生产环境(非 prod 环境),则执行日志输出。
        if (!SpringUtils.isProd()) { // 检查当前是否是生产环境,如果不是则记录日志
            // 获取请求的查询参数,以键值对的形式存储。
            Map<String, String> queryString = ServletUtils.getParamMap(request);

            // 如果请求是 JSON 请求类型,则从请求中提取出请求体。
            String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;

            // 如果既没有查询参数,也没有请求体,则打印无参数的日志;否则打印包含参数或请求体的日志。
            if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) {
                log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI());
            } else {
                log.info("[preHandle][开始请求 URL({}) 参数({})]", request.getRequestURI(),
                        StrUtil.nullToDefault(requestBody, queryString.toString()));
            }

            // 计时操作:为请求创建一个 StopWatch(计时器),并启动它,用于后续计算请求的处理耗时。
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 将 StopWatch 对象存储到 request 的属性中,以便在 afterCompletion 方法中使用。
            request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
        }
        return true; // 返回 true,表示允许请求继续往下处理。
    }

    /**
     * 拦截器的后置处理方法 (afterCompletion),在整个请求处理完成(包括视图渲染完成)之后调用。
     * 
     * @param request  当前的 HttpServletRequest 对象,包含了请求信息。
     * @param response 当前的 HttpServletResponse 对象,包含了响应信息。
     * @param handler  请求处理器,通常是一个 Controller 中的方法,或其对应的代理对象。
     * @param ex       如果请求处理过程中出现异常,这里可以捕获到。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 打印响应日志。如果当前环境不是生产环境(非 prod 环境),则执行日志输出。
        if (!SpringUtils.isProd()) {
            // 从 request 属性中获取在 preHandle 方法中创建的 StopWatch 对象。
            StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH);

            // 停止计时器,计算请求处理总耗时。
            stopWatch.stop();

            // 打印完成请求的日志,包括请求的 URL 和耗时(单位:毫秒)。
            log.info("[afterCompletion][完成请求 URL({}) 耗时({} ms)]",
                    request.getRequestURI(), stopWatch.getTotalTimeMillis());
        }
    }
}

截图如下:

在这里插入图片描述

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

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

相关文章

OJ在线评测系统 前端创建题目(增) 更新题目(改) 题目列表(查) 以及做题页面的开发 基于VUECLI脚手架画界面

目录 前端创建页面的开发一 创建一个路由 用acro design写 前端创建页面的开发二 题目管理页面 搜索 最終效果 题目更新页面的开发 携带参数的那种 修改路由 页码更新细节 我们先处理菜单项的权限控制和权限隐藏 在这里改 属性绑定一个函数 可以参考聚合搜索项目…

金属增材制造咋突破?纳米纹理粉末如何助力金属增材制造?

大家好&#xff0c;今天我们来了解一篇金属增材制造文章——《High absorptivity nanotextured powders for additive manufacturing》发表于《Science Advances》。金属增材制造在医疗、航空航天等领域&#xff0c;它潜力巨大&#xff0c;但目前可打印的金属材料有限&#xff…

word中的表格全部设置宽度100%

1、背景 我们用工具将数据库或其他的数据导出成word时&#xff0c;表格有的会大于100%&#xff0c;超过了边界。word没有提供全局修改的方法。如果我们想改成100%。 一种方式是通过宏&#xff0c;全局改。一种是手动改。 2、宏修改 如果表格多&#xff0c;可以通过这种方式。…

文献阅读——基于拉格朗日乘子的电力系统安全域边界通用搜索方法

摘要 为提升电力系统安全域(security region&#xff0c;SR)的构建效 率&#xff0c;提出一种基于拉格朗日乘子(Lagrange multiplier&#xff0c;LM) 的电力系统安全域边界(security region boundary&#xff0c;SRB)通用搜索方法。 首先&#xff0c;根据电力系统静态安全性问…

15.面试算法-树基础

1. 树基础 树在工程中有非常广泛的应用&#xff0c;在算法中也是一个极为庞大的体系&#xff0c;我们前面链表的文章中说过“没学会反转&#xff0c;链表相当于白学”&#xff0c;现在再加一句“没学会树的问题&#xff0c;算法相当于没学”。 本文我们将系统地学习相关问题。…

PLC程序加密的一种方法(密钥授权管理程序)

引文&#xff1a; 前段时间有客户跟我说&#xff0c;他们客户到期没有打款&#xff0c;所以想在PLC中开发一套授权管理程序&#xff0c;这样可以根据实际情况&#xff0c;给予客户不同的授权&#xff1b; 探讨&#xff1a; PLC怎么实现程序授权管理&#xff01; 对于国内工程师…

五星级可视化页面(28):3D园区—模型与数据的完美结合。

将园区3D模型和数据图表结合起来形成高大上的可视化效果有以下几方面的意义&#xff1a; 1. 提升信息表达效果&#xff1a; 通过将园区3D模型和数据图表结合&#xff0c;可以更生动地展现园区的空间结构和布局&#xff0c;同时将数据图表融入其中&#xff0c;直观地展示园区各…

Redis实战--Redis集群的搭建与使用

Redis是一个开源的高性能key-value数据库&#xff0c;它以其出色的性能和丰富的特性而广受欢迎。随着业务的发展&#xff0c;单机Redis可能无法满足大规模数据存储和高并发访问的需求。这时&#xff0c;Redis集群就显得尤为重要。本文将详细介绍Redis集群的概念、搭建过程以及使…

进阶美颜功能技术开发方案:探索视频美颜SDK

视频美颜SDK&#xff08;SoftwareDevelopmentKit&#xff09;作为提升视频质量的重要工具&#xff0c;越来越多地被开发者关注与应用。接下俩&#xff0c;笔者将深入探讨进阶美颜功能的技术开发方案&#xff0c;助力开发者更好地利用视频美颜SDK。 一、视频美颜SDK的核心功能 …

【CSS in Depth 2 精译_039】6.3 CSS 定位技术之:相对定位(上)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09;第二章 相对单位&#xff08;已完结&#xff09;第三章 文档流与盒模型&#xff08;已完结&#xff09;第四章 Flexbox 布局&#xff08;已…

IDEA插件:Maven Helper插件强势优化【某个依赖包被哪些maven项目模块引用,快速定位】体验真好!

背景&#xff1a; 开发的项目是maven多模块&#xff0c;子模块数量多&#xff0c;已经超过10个。 而且经常会被扫描漏洞&#xff0c;并进行依赖包升级。 在使用过程中&#xff0c;发现MavenHelper插件和IDEA自带的Analyze Dependencies都有个缺点&#xff1a;只能是单个模块…

【C++笔试强训】如何成为算法糕手Day3

​ 学习编程就得循环渐进&#xff0c;扎实基础&#xff0c;勿在浮沙筑高台 循环渐进Forward-CSDN博客 目录 循环渐进Forward-CSDN博客 第一题&#xff1a;除2&#xff01; 第二题&#xff1a;dd爱框框 第三题&#xff1a;简写单词 第一题&#xff1a;除2&#xff01; 牛客网…

前端独立实现页面是否有发布

1、自动更新js (AutoUpdate.js) import { Modal } from "antd"let lastSrcs; const scriptReg /\<script.*src["](?<src>[^"])/gm; async function extractNewScripts() {const html await fetch(/?_timnestamp Date.now()).then(res > …

追随 HarmonyOS NEXT,Solon v3.0 将在10月8日发布

Solon &#xff08;开放原子开源基金会&#xff0c;孵化项目&#xff09;原计划10月1日发布 v3.0 正式版。看到 HarmonyOS NEXT 将在 10月8日启用公测&#xff0c;现改为10月8日发布以示庆贺。另外&#xff0c;Solon 将在2025年启动“仓颉”版开发&#xff08;届时&#xff0c;…

(11)(2.1.2) DShot ESCs(四)

文章目录 前言 6 混合ESC协议 7 IOMCU DShot限制 8 参数说明 前言 DShot 是一种数字 ESC 协议&#xff0c;它允许快速、高分辨率的数字通信&#xff0c;可以改善飞行器控制&#xff0c;这在多旋翼和 quadplane 应用中特别有用。 6 混合ESC协议 虽然 ArduPilot 自动驾驶仪…

Redis 为什么用跳表实现有序集合

近几年针对 Redis 面试时会涉及常见数据结构的底层设计&#xff0c;其中就有这么一道比较有意思的面试题&#xff1a;“Redis 的有序集合底层为什么要用跳表&#xff0c;而不用平衡树、红黑树或者 B树&#xff1f;”。 本文就以这道大厂常问的面试题为切入点&#xff0c;带大家…

Python 将数据写入 excel(新手入门)

一、场景分析 假设有如下一组列表数据&#xff1a; 写一段 python脚本 将这组数据写入一个新建的 excel&#xff0c;表头是 【序号】、【姓名】、【性别】、【年龄】 student_list [{name:小林, gender:男, age:10}, {name:小红, gender:女, age:11}, {name:小王, gender:男…

mysql数据库:超键、候选键、主键与外键

mysql数据库&#xff1a;超键、候选键、主键与外键 1、超键&#xff08;Superkey&#xff09;2、候选键&#xff08;Candidate Key&#xff09;3、主键&#xff08;Primary Key&#xff09;4、外键&#xff08;Foreign Key&#xff09; &#x1f496;The Begin&#x1f496;点点…

【Golang】Go语言接口与多态

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

106.WEB渗透测试-信息收集-FOFA语法(6)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;105.WEB渗透测试-信息收集-FOFA语法&#xff08;5&#xff09; FOFA语法案例&#xff1a…