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

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

目录

拦截器简介

定义拦截器

注册配置拦截器

拦截器详解

拦截器的拦截路径配置

拦截器的执行流程

登录校验

定义拦截器

注册配置器

测试

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.hskmm.com/?act=detail&tid=36020

相关文章:

  • 如何将海量纸质表格一键数字化?表格识别技术给出答案
  • 10.21 NOIP 模拟赛 T1. 小 h 学步
  • 深入解析:大数据Spark(六十六):Transformation转换算子sample、sortBy和sortByKey
  • 完整教程:web前端团队开发code review方案最佳实践
  • 加密货币如何改变金融诈骗的游戏规则
  • 最大值的不同统计方法
  • 远程服务器显示pyQt界面
  • java的字符和字符串
  • python_日志记录-loguru
  • VSCode直观显示tensor形状
  • 软工第三次作业--结对作业
  • Day1HTML的基本骨架
  • 树上依赖性背包 学习笔记 | P6326 Shopping 题解
  • java标识符
  • 题解:uoj961【UR #30】赛场设计
  • 位运算快速卷积 快速沃尔什变换 FWT
  • 嵌合抗体:破解二抗选择难题,赋能多重分子检测的核心工具
  • 原来用聊天记录就可以创造数字分身!WeClone项目在Lab4AI平台上的复现
  • 自监督提示优化SPO
  • Java中的注释
  • 实测!不同场景下,哪款 AI IDE 能真正帮你少加班?
  • CSP-S模拟36 2025.10.21
  • 2025 年 AI 编程工具生成效果全景比拼:从技术实力到综合评分
  • 打造AI IDE标杆产品,腾讯CodeBuddy深度全方位解析
  • C语言项目开发常用目录结构 - Invinc
  • 2025年不锈钢水箱厂家权威推荐榜:方形/圆形/消防/生活/保温/承压/装配式/焊接水箱及水塔水罐全解析
  • day03-Coze记忆-对话体验
  • 2025年流量计厂家权威推荐榜单:电磁流量计、超声波流量计、涡街流量计、质量流量计专业制造商深度解析
  • RNDIS让Air8000的USB上网更智能、更快速!
  • 如果k8s有三个calico节点A,B,C 使用bgp模式的话是如何进行BGP对等会话的