Spring 统一机制处理 - 拦截器与适配器

news/2025/10/21 19:35:30/文章来源:https://www.cnblogs.com/lxjshuju/p/19156265

目录

拦截器简介

定义拦截器

注册配置拦截器

拦截器详解

拦截器的拦截路径配置

拦截器的执行流程

登录校验

定义拦截器

注册配置器

测试

DispatcherServlet 源码

初始化

处理请求(核心)

适配器模式

适配器模式的实现


拦截器简介

前面图书管理系统案例的完善中,我们实现了强制登录的功能,后端程序 Session 来判断用户是否登录。但实现过程还是比较麻烦的,且有大量冗余重复的代码。

有一种方法,可以统一拦截所有的请求,并进行 Session 校验 --> 拦截器。

拦截器:是 Spring 框架提供的核心特性之一,主要用来拦截用户的请求。在指定方法的前后,根据业务的需来执行我们预先设定好的代码。

定义拦截器

建立 HandlerInterceptor 接口,并重写所有途径

preHandle 方法:目标方法执行前执行,返回 true,继续执行后续操作。返回 false:中断后续操作。

postHandle 方法:目标方法执行后执行。

afterCompletion 途径:视图渲染完毕后执行,即最后执行。(前后端分离各自工程化,后端几乎不涉及视图,暂不了解~)

注册安装拦截器

建立 WebMvcConfigurer 接口,重写 addInterceptors 办法

当我们把 preHandle 办法中的返回值改为 false 的时候,再观察运行结果,发现拦截器拦截了请求,没有进行响应。

拦截器详解

拦截器的拦截路径配置

拦截路径,即我们定义的拦截器对那些请求生效。在注册配置拦截器的时候,通过 addPathPatterns() 方法,指定要拦截那些请求,也可以通过 excludePathPatterns() 指定不拦截那些请求。

在上面的 WebConfig 中,我们配置了 /**, 表示拦截所有的请求。

行添加 excludePathPatterns("/user/login") 设置不拦截登录的请求

还有一些其他的常见部署:

拦截器的执行流程

1. 添加拦截器后,在执行控制器层的代码之前,请求会先被拦截器拦截住,执行 preHandle 方法。这个方法会返回一个布尔类型的值,如果返回 true,则放行本次操作,执行 controller 层代码。如果返回 false,则不会放行,请求在此处被拦截,controller 层的代码不会被执行。

2. controller 当中的方法执行完毕后,再回过来执行 postHandle 以及 afterCompletion 方式。执行完毕之后,最终给浏览器响应数据。

登录校验

通过拦截器,大家补充完善图书管理系统中的登录校验功能。

定义拦截器

从 Session 中获取用户信息,如果 session 中不存在,则返回 false,并设置状态码为 401。

注册配置器

还有另一种形式的写法:

通过此时就能够将前面我们的登录校验代码删除掉了:

测试

此时打开 postman,在未登录情况下,输入 :http://127.0.0.1:8080/book/getListByPage

http 状态码为 401

当我们进行登录运行后http://127.0.0.1:8080/user/login?name=admin&password=admin

再访问 http://127.0.0.1:8080/book/getListByPage

资料进行了返回~

DispatcherServlet 源码

我们的服务器启动日志中: 有如下日志

Tomcat 启动后,有一个核心的类 DispatcherServlet,用来控制程序的执行程序。

所有的请求,都会先进入到 DispathcherServlet 中,执行 doDispatch 调度手段。如果有拦截器,会先执行拦截器 preHandle() 技巧的代码,如果 preHandle() 返回 true,继续访问 controller 中的手段。controller 中的方法执行完毕后,再执行 postHandle 和 atterCompletion,返回给 DispatcherServlet,最终给浏览器响应资料。

初始化

前面我们研究过 Servlet 的生命周期,知道起生命周期为 init service destory。

DispatcherServlet 根据其名字也可知道,是一个 Servlet,根据代码我们可以知道,还是 extend 的 Sevlet

DispatcherServlet 的初始化方法 init 是在其父类 HttpServletBean 中建立的。

主要作用是加载 web.xml 中的 DispathcerServlet 的配置,并调用子类初始化

父类 HttpServletBean 中的 init 方法源码如下:

为了方便阅读源码,我大致将其分为几个区域。

区域 1:源码的实现人员对该技巧做的总的注释。

我们允许通过翻译软件了解:

“将配置参数映射到此 Servlet 的 bean 属性,并且调用子类进行初始化”

“ServletException:如果 bean 属性无效(或者缺少必要的属性),或者子类初始化失败,抛出该异常”

区域 2:对下面的代码操作进行一个大致的解释:

从初始化参数中设置 bean 属性。(也就是总的注释中的第一句:将配置参数映射到 Servlet 的 bean 属性)

在源码中,我们可以看到记载了 ServletConfigPropertyValues 静态内部类,获得了一些参数,使用 BeanWrapper PropertyAccessorFactory 来构造了一个 DispatcherServlet,并对 DispatcherServlet 进行了一些属性的赋值。

区域 4:catch 中捕获到的一些异常,非主线流程,暂且可不看~

区域 5:译为让子类自行完成他们可能的初始化操作

区域 6:另一个初始化的方法

跳转到另一个初始化方法后:

途径上面的注释:子类可能重写这个方法以实现自定义初始化。在调用这个方法之前,此 Servlet 的所有 bean 属性都被设置。

继而这个方法直接看是空方法,大家点击左边的图标, 既行找到实现类。

总注释翻译为:覆盖了 HttpServletBean 中的方法,在设置任何 bean 属性之后调用。创建此 Servlet 的 WebApplicationContext。

即 HttpServletBean 的 inti 中调用了 initServletBean,initServletBean 在 FrameworkServlet 类中实现,主要作用是建立了 WebApplicationContext 容器(上下文),并加载 SpringMVC 配置文件中定义的 Bean 到该容器,最后将该容器添加到 ServletContext 中(红色框中的代码)

紫色框中的打印日志,正是我们控制台打印出来的日志

源码追踪技巧:

阅读框架源码时,一定抓住关键点,找到核心主线流程。

切忌从头到尾一行一行看。应该先从宏观上对整个流程或者整个原理有一个了解。

我们再研究上面红色框中的代码:initWebApplicationContext 方法

分开来看:

这段代码。

先判断“注入的 web 应用上下文(this.webApplicationContext)”是否存在,存在就使用,赋值给 wac

接着检查这个上下文是否属于可配备的 Web 应用上下文类型(ConfigurableWebApplicationContext),Configurable 为可配置的,且还未激活,!isActive,即还没有执行刷新流程(刷新流程大家下面会将)

如果这个上下文没有表明的上下文,就将根目录(rootContext) 上下文设置为它的父

最后调用 configureAndRefreshWebApplicationContext ,最这个 web 应用上下文做配置并触发刷新。

如果注入的 web 应用上下文不存在,执行 find 和 create 处理

if 中的 !refreshEventReceived 条件,判断是否收到刷新事件,如果没有,就执行 if 中的代码。

此时这里两种情况:要么上下文是不支持刷新的 ConfigurableApplicationContext,要么构造注入的时候,已经被刷新过了。

此时就要手动触发 onRefresh 方法,来完成初始化操作。

假如需要发布上下文,就把 Spring 的应用上下文(wac)放到 Servlet 中作为一个属性。这样 Web 应用中的其他组件就能方便的获取到这个上下文了。

再回过头看 onRefresh 办法

该方法属于模板方法,即支持子类重写以自定义逻辑。

点击左侧的小按钮找到实现类

实现类中调用了 initStrategies 方法,去初始化各种效果策略。

在 initStrategies 中进行了 9 大组件的初始化。

initMultipartResolver 处理文件上传的程序

initLocalResolver 处理语言 / 地区的工具

initHandlerMappings 处理请求映射到控制器的软件

initHandlerDapters 适配控制器逻辑的工具

initHandlerExceptionResolver 处理异常的工具

initrequestToViewNameTranslator 请求转换为视图名的工具

initViewResolvers 将逻辑视图转换为实际页面的工具

initFlashMapManager 管理重定向时数据传递的工具

如果没有配置相应的组件,则在 DispatcherServlet.properties 中有调整默认的策略

处理请求(核心)

DispatcherServlet 在接收到请求之后,执行 doDispatch 调度办法,再将请求转给 Controller

以下为 doDispatch 方法的具体实现

HandlerAdapter 在 Spring MVC 中使用了适配器模式,适配器模式能将两个不兼容的接口通过一定的方式使之兼容。

通过HandlerAdatper 用于拥护不同类型的处理器(Controller,HttpRequestHandler 或者 Servlet 等)让他们能够适配统一的请求处理流程。这样,Spring MVC 能够根据一个统一的接口来处理来自各种处理器的请求。

常见源码术语:

适配器模式

HandlerAdapter 在 Spring MVC 中使用了适配器模式

将一个类的接口,转换成客户期望可以使用的另一个接口,让原本接口不兼容的类允许合作无间,

即:若目标类不能直接启用,通过一个新的类,重新包装以下,适配调用方使用。使两个不兼容的接口借助一定的方式使之兼容。

如下:接口 A 和 接口 B 因参数类型,个数等原因不匹配

可以使用适配器的方式,使之兼容

角色:

Target:目标接口(可以是抽象类或接口),客户希望直接用的接口

Adaptee:适配者,与 Target 不兼容

Adapter:适配器类,此模式的核心,通过继承或者引用适配者对象,将其转变为目标接口

client:得利用适配器的对象

适配器模式的实现

我们前面打印日式使用的 slf4j 就使用了适配器模式。 slf4j 提供了一系列打印日志的 api,但底层调用的是 log4j 或者 logback 来打日志,我们作为 client,只需调用 slf4j 的 api 即可~

如下:现在有 Log4j

slg4j 接口:

当 client 想通过 Slf4jApi 直接调用 log4jLog 接口,因为方法名不同,无法直接调用

中间可以加一层适配器

在适配器中,实现了 Slaf4jApi,同时又持有了 Log4j 的实例

在使用中:

客户端想要使用 Slf4jApi 的方式打印日志。

先创建 Log4j 实例,再将 Log4j 实例传给 Log4jSlf4jAdapter 适配器,让适配器持有被适配者。

客户端再通过 Slf4jApi 接口的 log 方法发起调用(客户端期望的接口形式)

在适配器内部中,把 Slf4jApi 的 log 调用,转发给了持有的 Log4j 实例的 log4jLog 方法

一种补偿模式,用来补救设计上的缺陷,协调接口不兼容问题。就是适配器模式

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

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

相关文章

鸿蒙hdc命令【杭州多测师】

1、查看版本hdc -v2、查看连接设备hdc list targets3、安装应用hdc app install .\phone-launcher-default-signed.hap// 覆盖安装hdc app install -r .\phone-launcher-default-signed.hap// 安装、更新,多HAP可以指…

电脑黑屏只剩鼠标-解决方案 - 教程

电脑黑屏只剩鼠标-解决方案 - 教程2025-10-21 19:26 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impo…

如何将海量纸质表格一键数字化?表格识别技术给出答案

在当今的智能办公时代,海量的信息仍被困在纸质文档或僵化的电子图片中,其中,结构复杂、数据密集的表格是信息数字化的主要瓶颈之一。表格识别技术的崛起,正如同一位不知疲倦的“数据捕手”,精准地破解了这一难题,…

10.21 NOIP 模拟赛 T1. 小 h 学步

思路 最终答案为: [ ans = \sum_{i} \left( \left( \sum x_i \right)^2 + \left( \sum y_i \right)^2 + \left( \sum z_i \right)^2 \right) ] 其中对于同一个 ( i, x_i, y_i, z_i ) 互相关联。 将 ( x ) 部分的平方拆…

深入解析:【Linux】生产者消费者模型

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

湖南新建高速项目的“神经网络”是如何搭建的?——揭秘80公里高速的收费、通信、监控一体化系统

湖南新建高速项目的“神经网络”是如何搭建的?——揭秘80公里高速的收费、通信、监控一体化系统湖南省某条贯穿山区、串联景区的高速公路,不仅是交通主干道,更是区域协同发展和旅游经济助推器。全长约80公里,北起湘…

深入解析:大数据Spark(六十六):Transformation转换算子sample、sortBy和sortByKey

深入解析:大数据Spark(六十六):Transformation转换算子sample、sortBy和sortByKeypre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; …

完整教程:web前端团队开发code review方案最佳实践

完整教程:web前端团队开发code review方案最佳实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

实用指南:免费html网页模板 html5网站模板 静态网页模板

实用指南:免费html网页模板 html5网站模板 静态网页模板pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas…

最大值的不同统计方法

题目:CF2154B 2154B 题目中有这样一句话: 操作1: 选择一个整数 i 并执行 : 用最大值为 i的前缀替换 $ a_i $ 。 $$ \sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6} $$

加密货币如何改变金融诈骗的游戏规则

本文深入分析加密货币如何被诈骗分子利用实施新型金融犯罪,揭露了包括虚假赠品网站、匿名钱包洗钱、AI深度伪造等最新诈骗手法,并提供了5种实用防护策略保护个人资产安全。加密货币如何改变金融诈骗的游戏规则 加密货…

leetcode448. 找到所有数组中消失的数字

leetcode448. 找到所有数组中消失的数字448. 找到所有数组中消失的数字我的解法:额外数组class Solution {public List<Integer> findDisappearedNumbers(int[] nums) {int n = nums.length;boolean[] flag = n…

远程服务器显示pyQt界面

远程服务器显示pyQt界面1 vscode+mobaxterm实现服务器图形显示 在服务器上跑深度学习任务是有时候会需要可视化图片,但是服务器一般没有图形显示的功能: $ xclock Error: Cant open display: 可以通过 vscode+mobaxt…

揭开 C++ vector 底层面纱:从三指针模型到手写完整实现 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

java的字符和字符串

这是一个非常基础且重要的概念,用一个简单的比喻就很容易理解:字符 (Character): 就像一个“字母”或一个“汉字”。 字符串 (String): 就像一个“单词”或“一句话”。1. 字符 (Character)概念: 它是文本的最小、…

python_日志记录-loguru

日志记录-loguru1 使用 loguru 安装 pip install loguru如何使用 from loguru import loggerlogger.debug("This is a debug message") logger.info("This is an info message") logger.warning(&…

软工第三次作业--结对作业

廖永祺 谭钧灏 3123004628 GitHub项目地址:这个作业属于哪个课程 <班级的链接>这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class12Grade23ComputerScience/homework/13470这个作业的目标 熟悉小组…

原来用聊天记录就可以创造数字分身!WeClone项目在Lab4AI平台上的复现

通过给大模型喂我们的聊天记录,就可打造出我们的数字分身,当前爆火的Weclone项目采取的就是这种做法。01 | WeClone 如何创造数字分身 拥有一个数字分身可能是很多人的一个愿望。其实通过给大模型喂我们的聊天记录,…

Day1HTML的基本骨架

在vscode中,若是在.html后缀的文件可用快捷键帮助生成骨架,即按!的同时按住回车键或tab键 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta n…

结对项目作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13479这个作业的目标 实…