【SpringBoot】如何使用 HandlerInterceptor 拦截器? 为什么不使用 SpingAOP ?

news2024/11/24 19:52:10

文章目录

  • 前言
  • 一、为什么不使用 SpringAOP ?
    • 1, 需求分析
    • 2, SpringAOP 能实现吗?
  • 二、使用 HandlerInterceptor
    • 1, 实现 HandlerInterceptor 接口
    • 2, 将自定义拦截器加入到系统配置
  • 三、HandlerInterceptor 实现原理
    • 源码分析
  • 总结


前言

各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你:
📕 JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
📗 Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
📘 JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

在这里插入图片描述


提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎批评指点~ 废话不多说,直接上干货!


一、为什么不使用 SpringAOP ?

1, 需求分析

上篇文章 介绍了 AOP 和 SpringAOP , 接下来分析一个需求 : UserController 类里面定义了许多和用户相关的控制层代码, 在一层, 很多业务逻辑都是需要用户登录之后才能执行的

@RestController
@RequestMapping("/user")
public class UserController {
 	// 某⽅法 1
	@RequestMapping("/m1")
 	public Object method(HttpServletRequest request) {
 		// 有 session 就获取,没有不会创建
	 	HttpSession session = request.getSession(false);
	 	if (session != null && session.getAttribute("userinfo") != null) {
 		// 说明已经登录,进行业务处理
 			return true;
	 	} else {
		 	// 未登录
		 	return false;
	 	}
	 }
	 
 	// 某⽅法 2
	@RequestMapping("/m2")
 	public Object method2(HttpServletRequest request) {
 		// 有 session 就获取,没有不会创建
 		HttpSession session = request.getSession(false);
 		if (session != null && session.getAttribute("userinfo") != null) {
 			// 说明已经登录,业务处理
 			return true;
 		} else {
 			// 未登录
 			return false;
 		}
 	}
}

从上述代码可以看出, 每个方法中都有相同的用户登录验证权限, 它的缺点是:

    1. 每个方法中都要单独写⽤户登录验证的方法, 即使封装成公共方法, 也⼀样要传参调⽤和在方法中
      进⾏判断
    1. 添加控制器越多, 调用用户登录验证的方法也越多, 这样就增加了后期的修改成本和维护成本
    1. 这些⽤户登录验证的方法和接下来要实现的业务几乎没有任何关联, 但每个方法中都要写⼀遍

为了解决这一问题, 首先想到的就是 AOP 面向切面编程


2, SpringAOP 能实现吗?

上篇文章 介绍了 SpringAOP 的各种通知的使用方式, 在当前需求场景下, 使用前置通知或环绕通知 “应该” 是可以首先的, 但仔细一想就会有问题 :

  1. 要验证用户的登陆状态, 就要先获取到内存中的 session 对象, 但是通过前置或者环绕通知的方式时很难拿到请求对象的, 也就很难拿到 session 对象进行判断
  2. 与我们用户相关的控制器中并非所有方法都要进行拦截判断(像登录、注册方法, 通过原生 SpringAOP 的切点表达式配置拦截规则几乎是做不到的, 说白了就是无法定义哪些方法需要被拦截, 哪些方法不能被拦截

更好的方式是使用 HandlerInterceptor (拦截器)


二、使用 HandlerInterceptor

HandlerInterceptor 拦截器是将传统 AOP 进行了封装, 内置了 reuqest, response 对象, 提供了更加方便的功能

拦截器的实现分为以下两个步骤:

  1. 创建自定义拦截器,实现 HandlerInterceptor 接口的 preHandle(执行具体方法之前的预处理)方法
  2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法

1, 实现 HandlerInterceptor 接口

定义一个 LoginInterceptor 类表示登录拦截器, 实现 HandlerInterceptor 接口, 重写 preHandle()

@Component // 表示这是一个组件
public class LoginInterceptor implements HandlerInterceptor {

    private final String  SEEION_KEY = "SEEION";

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("SEEION") != null) {
            return true;
        }
        return false;
    }
}

preHandle() 是调用目标方法(要被拦截的方法)执行之前的方法, 此方法返回的是 boolean 类型的值

  • 如果返回的 true 表示可以放行, 允许继续走后续的流程, 执行目标方法
  • 如果返回 false, 表示不能放行, 不允许执行后续的流程和目标方法

2, 将自定义拦截器加入到系统配置

定义一个 APPConfig 类, 实现 WebMvcConfigurer 接口, 重写 addInterceptors()

addInterceptors() 这个方法需要将我们上面写好的自定义拦截器( LoginInterceptor 的 Bean 对象)加入到系统配置, 所以可以使用 @Autowired 进行注入

@Configuration // 表示这是一个配置
public class APPConfig implements WebMvcConfigurer {

    // 把拦截器那个 Bean 注入进来
    @Autowired
    private LoginInterceptor loginInterceptor;

    // 添加拦截器规则
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor) // 添加我们的自定义拦截器
                .addPathPatterns("/**") // 拦截所有url
                .excludePathPatterns("/user/login")// 放行这个url
                .excludePathPatterns("/login.html")// 放行这个url
                .excludePathPatterns("/reg.html")// 放行这个url
                .excludePathPatterns("/css/**")// 放行这个url
                .excludePathPatterns("/editor.md/**")// 放行这个url
                .excludePathPatterns("/editor.md/**")// 放行这个url
                .excludePathPatterns("/img/**")// 放行这个url
                .excludePathPatterns("/js/**");// 放行这个url
    }
}

1, addPathPatterns() 表示需要拦截的 url , **表示拦截任意 url
2, excludePathPatterns():表示需要排除(不拦截)的 url

说明:以上拦截规则可以拦截此项目中使用的 url, 包括静态文件件(图片, html, css, js 等)


三、HandlerInterceptor 实现原理

在使用拦截器之前, 用户访问 web 网站的前后端交互流程大致如下 :

在这里插入图片描述
前端的所有请求都会先来到 Controller 层, 但加入了拦截器, 会在 Controller 层之前工作
在这里插入图片描述


源码分析

  1. 所有的 Controller 层的执行都会通过一个 调度器 DispatcherServlet 来实现,这⼀点可以从 Spring Boot 控制台打印的日志信息看出,如下图所示
    在这里插入图片描述

  2. 而所有方法都会执行 DispatcherServlet 中的 doDispatch() 调度方法, doDispatch() 部分源码如下(只看重点)

// 预处理【重点】
if (!mappedHandler.applyPreHandle(processedRequest, respon se)) {
	return;
}
// 往后执⾏ Controller 中的业务
mv = ha.handle(processedRequest, response, mappedHandler.g
etHandler());
if (asyncManager.isConcurrentHandlingStarted()){
    return;
}

上述代码表示 : 开始执行 Controller 之前,会先调用 预处理方法 applyPreHandle()

applyPreHandle() 方法是Boolean 类型, 如果 applyPreHandle() 返回值为 true, 才能执行Controller 层的方法

  1. applyPreHandle() 的源码如下 :
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
 	for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
 		// 获取项⽬中使⽤的拦截器 HandlerInterceptor
 		HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
 		if (!interceptor.preHandle(request, response, this.handler)) {
 			this.triggerAfterCompletion(request, response, (Exception)null);
 			return false;
 		}
 	}
 	return true;
}

上述代码表示, 遍历 interceptorList (我们自定义拦截器的集合), 执行我们重写的 preHandle()
如果我们自定义的拦截器都返回 true 了, applyPreHandle() 才会返回 true , 才能执行 Controller 层的方法


总结

拦截器 HandlerInterceptor 相比于 SpringAOP 有两大优点 :

  1. preHandle() 可以轻松的获取并使用 request 和 response 对象
  2. addInterceptors() 中将自定义的拦截器加入到系统配置, addPathPatterns() 和 excludePathPatterns() 配合可以很自由的定义拦截规则

拦截器会在Controller 层之前执行, 执行我们定义的预处理逻辑

以上就是本篇的所有内容了, 如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦😋😋😋~


上山总比下山辛苦
下篇文章见

在这里插入图片描述

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

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

相关文章

基于全新电脑环境安装pytorch的GPU版本

前言&#xff1a; 距离第一次安装深度学习的GPU环境已经过去了4年多&#xff08;当时TensorFlow特别麻烦&#xff09;&#xff0c;现在发现安装pytorch的GPU版本还是很简单方便的&#xff0c;流程记录如下。 安装步骤&#xff1a; 步骤一&#xff1a;官网下载Anaconda Free…

【无法联网】电脑wifi列表为空的解决方案

打开电脑, 发现wifi列表为空, 点击设置显示未连接 首先检查是不是网卡驱动有问题, cmd, devmgmt.msc 找到网络适配器, 看看网卡前面是否有感叹号, 如果没有则说明网卡没问题, 有问题则重装驱动 看看网络协议是否设置正确 找到"控制面板\所有控制面板项\网络和共享中心&…

微服务通信[HTTP|RPC同步通信、MQ异步通信]

概念 A服务调用B服务,B服务调C服务,C服务调D服务,即微服务之间的通信(也可以叫微服务之间的调用) HTTP同步通信 一种轻量级的通信协议,常用于在不同的微服务之间进行通信,也是最简单的通信方式使用REST ful为开发规范&#xff0c;将服务对外暴露的HTTP调用方式为REST API(如GET…

飞腾FT-2000/4、D2000 log报错指导(3)

在爱好者群中遇见了很多的固件问题,这里总结记录了大家的交流内容和调试心得。主要是飞腾桌面CPU FT-2000/4 D2000相关的,包含uboot和UEFI。希望对大家调试有所帮助。 这个专题会持续更新,凑够一些就发。 23 在s3 唤醒时报错如下 check suspend ,Platform exception report…

你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢?

一、什么是SPA SPA&#xff08;single-page application&#xff09;&#xff0c;翻译过来就是单页应用SPA是一种网络应用程序或网站的模型&#xff0c;它通过动态重写当前页面来与用户交互&#xff0c;这种方法避免了页面之间切换打断用户体验在单页应用中&#xff0c;所有必…

【kubernetes】使用KubeSphere devops部署我的微服务系统

KubeSphere Devops 入门使用KubeSphere的Devops功能部署"我的微服务系统" &#xff08;内容学习于尚硅谷云原生课程&#xff09; kubesphere devops官方文档&#xff1a; https://v3-1.docs.kubesphere.io/zh/docs/devops-user-guide/how-to-use/create-a-pipeline-u…

nginx调优(二)

一、event模块: 用于配置服务器的事件驱动机制的模块。它定义了 Nginx 如何处理并发连接和网络事件&#xff0c;以及如何与底层操作系统的事件机制交互。 1.最大并发连接数&#xff1a; worker_connections 65536; 2.选择事件驱动&#xff1a; nginx默认使用epoll时间驱动类…

【Java】Java基础

环境准备 安装JDK和JRE 下载JDK&#xff0c;可以在官网Java Downloads | Oracle 中国下载&#xff0c;但是这里需要注册才能够下载。在Index of java-local/jdk (huaweicloud.com)也可以下载到&#xff0c;但是版本比较老&#xff0c;关系不大&#xff0c;直接下载&#xff0…

OpenGL-入门-BMP像素图glReadPixels

glReadPixels函数用于从帧缓冲区中读取像素数据。它可以用来获取屏幕上特定位置的像素颜色值或者获取一块区域内的像素数据。下面是该函数的基本语法&#xff1a; void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *da…

【Kafka】Kafka Stream简单使用

一、实时流式计算 1. 概念 一般流式计算会与批量计算相比较。在流式计算模型中&#xff0c;输入是持续的&#xff0c;可以认为在时间上是无界的&#xff0c;也就意味着&#xff0c;永远拿不到全量数据去做计算。同时&#xff0c;计算结果是持续输出的&#xff0c;也即计算结果…

微信小程序 基于Android的美容理发师预约管理系统

&#xff0c;本系统主要根据管理员、用户及理发师的实际需要&#xff0c;方便用户利用互联网实现对商品信息进行立即订购&#xff0c;同时让管理者可以通过这个系统对用户实际需求以及各信息进行管理。设计该系统主要目的是为了方便用户、理发师可以有一个非常好的平台体验&…

Mac下Docker Desktop开启本地远程访问

mac系统下&#xff0c;为了在idea里方便使用docker&#xff0c;需要开启Docker Desktop本地远程访问。 开启方法是在设置-高级下&#xff0c;开启“Allow the default Docker socket to be used (requires password)”&#xff0c;特此记录一下&#xff1a; 开启后的效果&…

iOS swift5 扫描二维码

文章目录 1.生成二维码图片2.扫描二维码&#xff08;含上下扫描动画&#xff09;2.1 记得在info.plist中添加相机权限描述 1.生成二维码图片 import UIKit import CoreImagefunc generateQRCode(from string: String) -> UIImage? {let data string.data(using: String.En…

计算机视觉:深层卷积神经网络的构建

本文重点 上一节课程中我们学习了单卷积层的前向传播,本次课程我们构建一个具有三个卷积层的卷积神经网络,然后从输入(39*39*3)开始进行三次卷积操作,我们来看一下每次卷积的输入和输出维度的变化。 第一层 第一层使用3*3*3的过滤器来提取特征,那么f[1]=3,然后步长s[…

4. 池化层相关概念

4.1 池化层原理 ① 最大池化层有时也被称为下采样。 ② dilation为空洞卷积&#xff0c;如下图所示。 ③ Ceil_model为当超出区域时&#xff0c;只取最左上角的值。 ④ 池化使得数据由5 * 5 变为3 * 3,甚至1 * 1的&#xff0c;这样导致计算的参数会大大减小。例如1080P的电…

PHP8的匿名函数-PHP8知识详解

php 8引入了匿名函数&#xff08;Anonymous Functions&#xff09;&#xff0c;它是一种创建短生命周期的函数&#xff0c;不需要命名&#xff0c;并且可以在其作用域内直接使用。以下是在PHP 8中使用匿名函数的知识要点&#xff1a; 1、创建匿名函数&#xff0c;语法格式如下&…

7.react useReducer使用与常见问题

useReducer函数 1. useState的替代方案.接收一个(state, action)>newState的reducer, 并返回当前的state以及与其配套的dispatch方法2. 在某些场景下,useReducer会比useState更加适用,例如state逻辑较为复杂, 且**包含多个子值**,或者下一个state依赖于之前的state等清楚us…

postgresql-日期函数

postgresql-日期函数 日期时间函数计算时间间隔获取时间中的信息截断日期/时间创建日期/时间获取系统时间CURRENT_DATE当前事务开始时间 时区转换 日期时间函数 PostgreSQL 提供了以下日期和时间运算的算术运算符。 计算时间间隔 age(timestamp, timestamp)函数用于计算两…

Uniapp笔记(五)uniapp语法4

本章目标 授权登录【难点、重点】 条件编译【理解】 小程序分包【理解】 一、授权登录 我的模块其实是两个组件&#xff0c;一个是登录组件&#xff0c;一个是用户信息组件&#xff0c;根据用户的登录状态判断是否要显示那个组件 1、登录的基本布局 <template><…

LLMs NLP模型评估Model evaluation ROUGE and BLEU SCORE

在整个课程中&#xff0c;你看到过类似模型在这个任务上表现良好&#xff0c;或者这个微调模型在性能上相对于基础模型有显著提升等陈述。 这些陈述是什么意思&#xff1f;如何形式化你的微调模型在你起初的预训练模型上的性能改进&#xff1f;让我们探讨一些由大型语言模型开…