SpringBoot 统一功能处理

embedded/2025/2/15 21:08:48/

1. 拦截器

        引入

        上个章节我们完成了强制登录的功能, 后端程序根据Session来判断用户是否登录, 但是实现⽅法是⽐较麻烦的

• 需要修改每个接⼝的处理逻辑

• 需要修改每个接⼝的返回结果

• 接⼝定义修改, 前端代码也需要跟着修改

有没有更简单的办法, 统⼀拦截所有的请求, 并进⾏Session校验呢, 这⾥我们学习⼀种新的解决办法: 拦截

        概念:

        拦截器是Spring框架提供的核⼼功能之⼀, 主要用来拦截用户的请求, 在指定⽅法前后, 根据业务需要执⾏预先设定的代码.(一般拦截的是controller里面的)

        也就是说, 允许开发⼈员提前预定义⼀些逻辑, 在⽤⼾的请求响应前后执⾏. 也可以在⽤⼾请求前阻⽌其执⾏.

        在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作, ⽐如通过拦截器来拦截前端发来的请求, 判断Session中是否有登录用户的信息. 如果有就可以放行, 如果没有就进⾏拦截.

        

        学习拦截器的使用

        1> 定义拦截器(定义一下这个拦截器做什么工作,岗位职责)

        2> 注册配置拦截器(设置拦截器,哪些拦截,哪些不拦截)

         初步的体验如何使用

        HandlerInterceptor,Hander表示处理器,Interceptor表示拦截器,在这个类里面我们后续会补充拦截的逻辑

        源码分析

        在这个代码中,我们对拦截器进行注册,实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法
 

        我们来看看不同语句打印的时间是如何

        拦截器详细解释

                拦截路径是指我们定义的这个拦截器, 对哪些请求⽣效.我们在注册配置拦截器的时候, 通过 addPathPatterns() ⽅法指定要拦截哪些请求. 也可以通过excludePathPatterns() 指定不拦截哪些请求.

        然后我们回到config.我们肯定不可以把所有的请求都排除掉,我们登录注册这种请求肯定要有的.

        然后我们看看运行user/login之后有没有打印(发现确实是没有打印)

        拦截器的调用流程

        正常的调用

        有了拦截器之后的调用:有了拦截器之后,会在调⽤ Controller 之前进⾏相应的业务处理

          文字描述版本

1. 添加拦截器后, 执⾏Controller的⽅法之前, 请求会先被拦截器拦截住. 执⾏ preHandle() ⽅法,这个⽅法需要返回⼀个布尔类型的值. 如果返回true, 就表⽰放⾏本次操作, 继续访问controller中的⽅法. 如果返回false,则不会放⾏(controller中的⽅法也不会执⾏).

2. controller当中的⽅法执⾏完毕后,再回过来执⾏ postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据.

实现登录校验拦截器

                HttpSession session = request.getSession(false)这行代码的参数所代表的意思

        我们拦截器的内部逻辑实现

        此时我们可以看见,除了登录页面其他页面都访问不进去

        然后我们还要加一点,也就是判断没有登录之后我们要跳转到登录页面,此时我们需要修改前端代码,结果如下:我们访问book_list.html就直接跳转到登录页面了

        

        日志解释

        tomcat是一个web项目的容器,可以装n个项目,然后项目和项目之间使用context path来进行区分.但是spring把tomcat集成进来之后,tomcat就只能启动一个项目了,因此context path为空     

        具体url路径

        

        关于servlet我们补充一下

Spring是基于servlet开发的,servlet的生命周期(可能是面试题)  

         init: 初始化九大组键

         service: 当请求来的时候具体做的工作是什么(业务的具体逻辑,会来回调用,现在用controller来写) 

        destory: 销毁

         

        拦截器源码分析:

        DispatcherServlet源码 

        当Tomcat启动之后, 有⼀个核⼼的类DispatcherServlet, 它来控制程序的执⾏顺序.DispatcherServlet,这个是一个调度servlet,调度整个spring,主流程.我们阅读源码就是这么一个过程,先找到主流程,然后根据单词猜意思.(连猜带过)

        ctrl+alt+<-表示上一个刚刚看的,ctrl+alt+->表示下一次代码(来回可以切换)

        看源码某个变量主流程怎么用,我们也可以debug,然后跑一下我们的程序,根据注释连蒙带猜,打日志的不看

        源码分析

        适配器模式

        概念

        适配器模式, 也叫包装器模式. 将⼀个类的接⼝,转换成期望的另⼀个接⼝, 适配器让原本接⼝不兼容的类可以合作⽆间.

        简单来说就是⽬标类不能直接使用, 通过⼀个新类进⾏包装⼀下, 适配调用方使⽤. 把两个不兼容的接⼝通过⼀定的⽅式使之兼容.

        ⽐如下⾯两个接⼝, 本⾝是不兼容的(参数类型不⼀样, 参数个数不⼀样等等)

        适配器模式角色

         • Target: ⽬标接⼝ (可以是抽象类或接⼝), 客户希望直接⽤的接⼝

        • Adaptee: 适配者, 但是与Target不兼容

        • Adapter: 适配器类, 此模式的核⼼. 通过继承或者引⽤适配者的对象, 把适配者转为⽬标接⼝

        • client: 需要使⽤适配器的对象

        通过适配器类把适配者转换为用户要的目标接口

        适配器模式的实现

        适配器模式的应用场景

        ⼀般来说,适配器模式可以看作⼀种"补偿模式",用来补救设计上的缺陷. 应⽤这种模式算是"⽆奈之举", 如果在设计初期,我们就能协调规避接⼝不兼容的问题, 就不需要使⽤适配器模式了

        所以适配器模式更多的应⽤场景主要是对正在运⾏的代码进⾏改造, 并且希望可以复⽤原有代码实现新的功能. ⽐如版本升级等.

        注意:idea中要找某个类或者方法,可以连续按俩下shift,会弹出搜索框

        Spring初始化模块

        init初始化九大主键(这样我们才知道我们url对应的是哪个方法,把它交给谁来处理,主要还是适配器来进行处理,通过适配器找到目标类(adapter调用controller))

        我们着重介绍三个

        initHandlerMappings(context);处理器映射(我们客户端里面的url里面的/xx/xx,是一个字符串,映射到spring项目controller里面@RequestMapping("/xx)这个就是HandlerMapping),url->哪个controller里面的方法

        initHandlerAdapters(context);处理器适配器(我们的spring是基于servlet实现的,不仅支持controller还支持servlet等,而我们的url对应的可能是controller可能是servlet,此时就需要一个适配器进行适配)

        initHandlerExceptionResolvers(context);异常解析器

        一些专业名词;

        dispatch(调度),adapter(适配器),context(上下文),handler(处理器),Resovler(解析器),apply(应用/执行),setAttribute(设置属性),mapping(映射/路由),mulpart(文件相关),interceptor(拦截器),Chain(链)

        service

        

2. 统⼀数据返回格式

        引入

        强制登录案例中, 我们共做了两部分⼯作

        1. 通过Session来判断用户是否登录

        2. 对后端返回数据进⾏封装, 告知前端处理的结果

        后端统一返回结果

        后端逻辑处理

        Result.success(pageResult) 就是对返回数据进⾏了封装

拦截器帮我们实现了第⼀个功能, 接下来看SpringBoot对第⼆个功能如何⽀持

        第二个功能的思路如图,但是如果我们每一个都改成Result类型,这是个重复性工作,因此我们提到了统一返回格式,我们将学习如何对数据进行统一的封装

        

        具体使用

        此时我们就要学习统一的数据返回格式

        统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现 @ControllerAdvice 表⽰控制器通知类.这个body(表示请求的正文:date,content-type..)   

        如果设置为false,就不会对结果进行统一处理            

        添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加@ControllerAdvice 注解

        我们对每个格式都进行处理(String必须单独处理,后续会详细解释),把String类型转换成Result类型需要一个信息转换器(messageConverters)

        然后我们测试其他返回类型

        如过不对Sting进行单独处理,会报错,类型不匹配,需要的是String类型,但是返回的是Result类型

        阅读源码

       具体的报错:类型不匹配

        具体解释,我们读源码,这个是不加String类型的流程

        加了String类型处理的流程

ctrl+alt+b进入方法实现,如果接口实现或者子类,就会直接跳转到子类或者实现类,f9下一个断点,f8下一步,f7进入方法

        我们详细读一下这个代码

        主要读hadlerMapping和Adapter

        关于MessageConvert的补充: 我们的信息转换器有很多,那么是如何判断哪个转换器可以处理?         (后续再补充)

        统一数据返回的优点优点:

        1. 方便前端程序员更好的接收和解析后端数据接⼝返回的数据

        2. 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现就可以了, 因为所有接⼝都是这样返回的.

        3. 有利于项⽬统⼀数据的维护和修改.

        4. 有利于后端技术部门的统⼀规范的标准制定, 不会出现稀奇古怪的返回内容.

3. 统一异常处理

        引入

        在图书管理系统里面,我们BookController里面是有异常处理的,但是有一些接口我并没有进行异常处理,这样的话很有可能会报错,会抛给调用方.(我们还是要把异常进行捕获),因此在这个程序中不管什么样的异常,只要能够捕获,就不要进行处理.(不管内部产生了什么错误,我们不能够让外部人员感知到,不能发送500,404等等信息,我们统一返回一个错误信息)

        可以来看看b站404的处理

        具体使用

        统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实的,@ControllerAdvice 表⽰控制器通知类, @ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件.

        以上代码表⽰,如果代码出现Exception异常(包括Exception的⼦类), 就返回⼀个 Result的对象, Result对象的设置参考 Result.fail(e.getMessage())
 

        加上@ResponseBody和不加的返回结果,我们404不一定是url写错了,有可能是因为没有写注解.

        进行测试,我们模拟制造异常(记得关闭拦截器和统一结果返回)

         

后端返回

        我们也可以对异常进行细分(如果有我们写的异常捕获就会被那个捕获,如果没有具体写的异常捕获,那么久直接使用它的上一层捕获(Exception),优先子类捕获,子类捕获不到就父类进行捕获

        另一种写法

        阅读源码

        @ControllerAdvice 源码分析统⼀数据返回和统⼀异常都是基于 @ControllerAdvice 注解来实现的, 通过分析@ControllerAdvice 的源码, 可以知道他们的执⾏流程.点击 @ControllerAdvice 实现.Advice可以这么理解(执行的具体逻辑是什么)

        源码如下:

        从上述源码可以看出 @ControllerAdvice 派⽣于 @Component 组件, 这也就是为什么没有五⼤注解, ControllerAdvice 就⽣效的原因.

        下⾯我们看看Spring是怎么实现的, 还是从 DispatcherServlet 的代码开始分析.DispatcherServlet 对象在创建时会初始化⼀系列的对象:

        

         对于 @ControllerAdvice 注解,我们重点关注 initHandlerAdapters(context) 和initHandlerExceptionResolvers(context) 这两个⽅法.

        1> initHandlerAdapters(context)

        initHandlerAdapters(context) ⽅法会取得所有实现了 HandlerAdapter 接⼝的bean并保存起来,其中有⼀个类型为 RequestMappingHandlerAdapter 的bean,这个bean.@RequestMapping 注解能起作⽤的关键,这个bean在应⽤启动过程中会获取所有被@ControllerAdvice 注解标注的bean对象, 并做进⼀步处理,关键代码如下:

        这个⽅法在执⾏时会查找使⽤所有的 @ControllerAdvice 类,把 ResponseBodyAdvice 类放在容器中,当发⽣某个事件时,调⽤相应的 Advice ⽅法,⽐如返回数据前调⽤统⼀数据封装

        2> initHandlerExceptionResolvers(context)

        接下来看 DispatcherServlet 的 initHandlerExceptionResolvers(context) ⽅法,这个⽅法会取得所有实现了 HandlerExceptionResolver 接⼝的bean并保存起来,其中就有⼀个类型为 ExceptionHandlerExceptionResolver 的bean,这个bean在应⽤启动过程中会获取所有被 @ControllerAdvice 注解标注的bean对象做进⼀步处理,代码如下:

        当Controller抛出异常时, DispatcherServlet 通过ExceptionHandlerExceptionResolver 来解析异常,而ExceptionHandlerExceptionResolver ⼜通过 ExceptionHandlerMethodResolver来解析异常, ExceptionHandlerMethodResolver 最终解析异常找到适⽤的@ExceptionHandler标注的⽅法是这⾥:

        ExceptionHanlderExceptioResolver和@ExceptionHandler对应的上.我们这里介绍一下afterPropertiesSet()方法.(后置处理器)Spring一开始先根据五大注解加载Bean,在加载完之后,再从那些bean里面找HandlerExceptionResolver的实现,然后执行一些额外的初始化.bean的初始化执行后的方法就是afterPropertiesSet() 

        判断不同的类型,然后加到不同的地方去

整体流程

      然后我们对异常处理,它怎么分辨是哪个异常被怎么处理.这个操作进行详细解释

上面的统一结果的返回都是后端代码,我们现在对前端代码也进行修改

        修改登录接口

总结:

1. 拦截器的实现主要分两部分: 1. 定义拦截器(实现HandlerInterceptor 接⼝) 2. 配置拦截器

2. 统⼀数据返回格式通过@ControllerAdvice + ResponseBodyAdvice 来实现

3. 统⼀异常处理使⽤@ControllerAdvice + @ResponseBody+@ExceptionHandler 来实现, 并且可以分异常来处理

4. 学习了DispatcherServlet的⼀些源码.


http://www.ppmy.cn/embedded/162503.html

相关文章

NLP Word Embeddings

Word representation One-hot形式 在上一周介绍RNN类模型时&#xff0c;使用了One-hot向量来表示单词的方式。它的缺点是将每个单词视为独立的&#xff0c;算法很难学习到单词之间的关系。 比如下面的例子&#xff0c;即使语言模型已经知道orange juice是常用组合词&#xf…

3.【线性代数】——矩阵乘法和逆矩阵

三 矩阵乘法和逆矩阵 1. 矩阵乘法1.1 常规方法1.2 列向量组合1.3 行向量组合1.4 单行和单列的乘积和1.5 块乘法 2. 逆矩阵2.1 逆矩阵的定义2.2 奇异矩阵2.3 Gauss-Jordan 求逆矩阵2.3.1 求逆矩阵 ⟺ \Longleftrightarrow ⟺解方程组2.3.2 Gauss-Jordan求逆矩阵 1. 矩阵乘法 1.…

C++自研游戏引擎-碰撞检测组件-八叉树AABB检测算法实现

八叉树碰撞检测是一种在三维空间中高效处理物体碰撞检测的算法&#xff0c;其原理可以类比为一个管理三维空间物体的智能系统。这个示例包含两个部分&#xff1a;八叉树部分用于宏观检测&#xff0c;AABB用于微观检测。AABB可以更换为均值或节点检测来提高检测精度。 八叉树的…

2024 StoryDiffusion 文字/文字+图像----->视频

基于扩散模型的生成模型在生成长序列图像和视频时面临内容一致性的重大挑战&#xff0c;尤其是涉及复杂主题和细节的场景中&#xff0c;角色身份、服饰风格等元素难以保持连贯。传统方法通常依赖潜在空间的运动预测&#xff0c;但长视频生成时易出现不稳定性。针对这些问题&…

初学 mybatis

前言 回顾之前 不使用 mybatis 框架&#xff0c;我们是怎么通过Java 操作数据库的 "jdbc" 前提&#xff1a;使用maven 构建的项目 1 添加 关于jdbc 的依赖&#xff0c;以及辅助操作数据库的 commons-dubli jar包 截取 前后端项目 2 添加配置文件里面内容有&…

上下文编辑器在不同场景下的功能(含使用案例)

上下文编辑器&#xff08;Context Editor&#xff09;解释 上下文编辑器&#xff08;Context Editor&#xff09;通常指的是一种能够修改、优化或过滤上下文信息的工具或方法&#xff0c;以增强下游任务的表现&#xff0c;特别是在 检索增强生成&#xff08;RAG&#xff09;、问…

Webpack代码分割、分割策略性能优化详解

在前端面试中,Webpack 是一个常见的考察点,特别是关于性能优化、构建配置以及代码分割等方面的问题。以下是 Webpack 常见问题详解,包括 代码分割 相关的内容。 1. Webpack 基础概念 1.1 Webpack 是什么? Webpack 是一个前端构建工具,主要用于将项目中的各种资源(JavaS…

《Nuxt.js 实战:从放弃到入门》一、项目初始,图片尺寸缩放

环境准备 在开始之前&#xff0c;确保你的开发环境已经安装了以下工具&#xff1a; Node.js&#xff1a;建议安装最新的 LTS 版本&#xff0c;可以从 Node.js 官网 下载安装。npm 或 yarn&#xff1a;npm 会随着 Node.js 一起安装&#xff0c;yarn 可以通过 npm install -g y…