Spring -- 拦截器

news2024/11/15 1:31:46

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 今天你敲代码了吗

文章目录

      • 1. 自定义拦截器
      • 2. 注册配置拦截器
      • 3. 拦截器详解
        • 3.1 拦截路径
        • 3.2 拦截器执行流程
      • 3.3 DispatcherServlet源码分析
        • 3.3.1 初始化:
        • 3.3.2 处理请求
        • 3.3.3 适配器


拦截器是Spring框架提供的核心功能,主要用来拦截用户的请求,在指定方法前后执行预先设定的代码(如判断用户是否登录等)image.png

以图书管理系统作为案例展示:
有一个登录接口:
image.png
以及图书信息的相关接口:
image.png
希望能做到,在用户直接访问图书信息接口的时候,进行拦截,判定用户是否登录,如果已经登录则放行,未登录则拦截,跳转到登录页面

1. 自定义拦截器

实现HandlerInterceptor接口,并重写所有方法

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("拦截:检验用户是否登录");
        HttpSession session = request.getSession();
        UserInfo userInfo = (UserInfo) session.getAttribute(Constants.USER_INFO_SESSION_KEY);
        if (userInfo == null) {
            log.info("校验失败");
            response.setStatus(401);
            response.getOutputStream().write("noLogin".getBytes());
            return false;
        }
        log.info("校验成功");
        return true;
    }
}

2. 注册配置拦截器

实现WebMvcConfigurer接口,并重写addInterceptor方法

@Configuration
public class WebConfig implements WebMvcConfigurer {
    //自定义的拦截对象
    @Autowired
    private LoginInterceptor loginInterceptor;

    //addPath--添加拦截对象
    //excludePath -- 排除拦截对象(如登录接口不能被拦截
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**").
        excludePathPatterns("/user/login").
        excludePathPatterns("/css/**").
        excludePathPatterns("/js/**").
        excludePathPatterns("/imgs/**").
        excludePathPatterns("/**/*.html").excludePathPatterns("/**/*.ico");
    }
}

当我们直接访问图书相关接口的时候:
image.png
就能起到拦截效果

3. 拦截器详解

3.1 拦截路径

拦截路径用来定义拦截器对哪些请求生效
通过addPathPatterns()方法指定要拦截哪些请求,通过 excludePathPatterns()指定哪些请求不拦截

拦截路径含义举例
/*一级路径能匹配/user, /book,但是不能匹配/user/login
/**任意级路径能匹配/user, /use/login
/book/*/book下的一级路径能匹配/book/addBook,
不能匹配/book/addBook/add
/book/**/book下的任意级路径能匹配/book/addBook,
也能匹配/book/addBook/add

3.2 拦截器执行流程
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("目标方法前执行....");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("目标方法后执行....");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("视图渲染完后执行....");
    }
}

访问后:
image.png

  • preHandle():目标方法执行前执行,返回true则继续执行后续操作;返回false则中断后续操作
  • postHandle():目标方法执行后执行
  • afterCompletion():视图渲染完后执行,最后执行(现在前后端分离后基本不涉及了)

3.3 DispatcherServlet源码分析

当Tomcat启动的时候,有一个核心的类DispatcherServlet,用来控制程序的执行顺序
image.png
所有请求都会先进到DispatcherServlet,执行doDisPatch调度方法

3.3.1 初始化:

DispatcherServlet的初始化方法init是在其父类FrameworkServlet的父类HttpServletBean中实现的,源码如下:

@Override
public final void init() throws ServletException {

// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
    try {
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
        ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
        bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
        initBeanWrapper(bw);
        bw.setPropertyValues(pvs, true);
    }
    catch (BeansException ex) {
        if (logger.isErrorEnabled()) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
        }
        throw ex;
    }
}

// Let subclasses do whatever initialization they like.
initServletBean();
}

实际上这个方法就是在初始化Servlet,包括初始化DispatcherServlet
在这个方法的最后,调用了initServletBean方法,是在FrameworkServlet里面实现的
主要的作用就是建立WebApplicationContext容器(即上下文),用来存储Bean

@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
    logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();

try {
    this.webApplicationContext = initWebApplicationContext();
    initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
    logger.error("Context initialization failed", ex);
    throw ex;
}

if (logger.isDebugEnabled()) {
    String value = this.enableLoggingRequestDetails ?
    "shown which may lead to unsafe logging of potentially sensitive data" :
    "masked to prevent unsafe logging of potentially sensitive data";
    logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
                 "': request parameters and headers will be " + value);
}

if (logger.isInfoEnabled()) {
    logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}

我们平时启动项目自动打印的日志,有一部分就是这里实现的(这一部分是在接受请求后才能打印出来的)
image.png
在这个方法内,调用了initWebApplicationContext方法初始化Bean容器的过程中,调用了onRefresh方法,用来初始化SpringMVC的容器,就是存储的Controller之类的很多Bean

3.3.2 处理请求

此时接受到的请求是在DispatcherServlet.doDispatch进行处理的

@SuppressWarnings("deprecation")
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            //处理请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Determine handler for the current request.
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            //获取适配器(后文解释)
            // Determine handler adapter for the current request.
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = HttpMethod.GET.matches(method);
            if (isGet || HttpMethod.HEAD.matches(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            //执行拦截器的preHandle方法
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            //执行目标方法
            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);

            //执行拦截器的postHandle方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new ServletException("Handler dispatch failed: " + err, err);
        }

        //处理视图 在这个方法里面执行拦截器的afterCompletion方法
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        //捕获异常也要执行拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
         //捕获异常也要执行拦截器的afterCompletion方法
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                               new ServletException("Handler processing failed: " + err, err));
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        }
        else {
            // Clean up any resources used by a multipart request.
          if (multipartRequestParsed) {
             cleanupMultipart(processedRequest);
          }
       }
    }
}

我们来看applyPreHandle方法:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
        }
        this.interceptorIndex = i;
    }
    return true;
}

此时就会获取到所有的拦截器,执行拦截器里面的preHand方法

3.3.3 适配器

HandlerAdapter主要用于支持不同类型的处理器,在Spring MVC中,处理器有很多种,如基于JavaBean的控制器,基于注解的控制器(如@Controller)等等,每个控制器可能需要不同的方式来调用,为了让他们能够适配统一的请求流程,就使用了HandlerAdapter,这样Spring MVC就能通过一个统一的接口来处理来自各种控制器的请求
这就涉及到设计模式之一 – 适配器模型的思想


感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 今天记得敲代码

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

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

相关文章

vue项目删除无用的依赖

1.安装依赖检查工具 npm i depcheck 2.查看无用的依赖 npx depcheck 3.手动删除pageage.json中的无用的依赖&#xff08;如果有sass和sass-loader不要删&#xff0c;会引起项目报错&#xff09; 4.全部删除完成之后&#xff0c;删除package-lock.json和yarn.lock文件&#x…

【文件解析漏洞复现】

一&#xff0e;IIS解析漏洞复现 1.IIS6.X 方式一&#xff1a;目录解析 搭建IIS环境 在网站下建立文件夹的名字为.asp/.asa 的文件夹&#xff0c;其目录内的任何扩展名的文件都被IIS当作asp文件来解析并执行。 访问成功被解析 方式一&#xff1a;目录解析 在IIS 6处理文件解…

图纸加密与零信任沙箱:构建企业数据安全的双重保障

在这个信息爆炸的时代&#xff0c;数据安全如同一场没有硝烟的战争。深信达SDC沙盒防泄密系统&#xff0c;以其零信任沙盒技术&#xff0c;为企业提供了一个坚不可摧的“金钟罩铁布衫”&#xff0c;确保企业图纸安全“坚如磐石”。 一、数据安全的“冰与火之歌” 数据安全是一…

如何简便改文件名

在出OI题的时候&#xff0c;有时候想要方便地把输入输出文件的文件名都改掉&#xff0c;类似于将a1.in,a2.in,…,a50.in都改成b1.in,b2.in,…,b50.in 我用gpt写了一个python代码 import osdef rename_files(base_name, new_name, num_files):for i in range(1, num_files 1)…

函数实例讲解 (一)

文章目录 函数中的引用、运算符、通配符1、引用2、运算符3、通配符 函数的类别、输入方式、结果检查1、函数类别2、输入方式3、结果检查 数组的基本概念1、数组极其元素的概念2、数组的书写3、数组的类型4、内存数组的存储位置5、数组公式与普通公式的区别 逻辑判断函数之IF1、…

【工具】-gdb-学习笔记

准备工作 程序的发布方式有两种&#xff0c;debug模式和release模式 Linux gcc/g出来的二进制程序&#xff0c;默认是release模式 要使用gdb调试&#xff0c;必须在源代码生成二进制程序的时候, 加上 -g 选项&#xff0c;发布成debug模式 如下源代码经过编译&#xff1a; #…

VS Code C/C++ MSVC编译器

官方教程 通过快捷方式打开VS Code是编译不了的,需要对tasks.json修改(Tasks: Configure default build task) 先创建tasks.json 复制这段配置到tasks.json,记得修改VsDevCmd.bat的路径 {"version": "2.0.0","windows": {"options"…

深度学习中卷积算子和dropout算子的作用

笔者在调网络的时候&#xff0c;有时调细一些在想不同卷积核尺寸的卷积操作有啥区别&#xff0c;在哪些算子后用dropout会比较好呢&#xff1f;于是有了下面一段总结。 文章目录 一、卷积核尺寸1X1和3X3的区别1x1卷积核3x3卷积核 二、dropout的作用使用情况算子组合注意事项 一…

[读论文]-FreeU: Free Lunch in Diffusion U-Net 提高生成质量

摘要 In this paper, we uncover the untapped potential of diffusion U-Net, which serves as a “free lunch” that substantially improves the generation quality on the fly. We initially investigate the key contributions of the U-Net architecture to the denois…

《计算机网络》(第8版)第9章 无线网络和移动网络 复习笔记

第 9 章 无线网络和移动网络 一、无线局域网 WLAN 1 无线局域网的组成 无线局域网提供移动接入的功能&#xff0c;可分为两大类&#xff1a;有固定基础设施的和无固定基础设 施的。 &#xff08;1&#xff09;IEEE 802.11 IEEE 802.11 是无线以太网的标准&#xff0c;是有固定…

SQL数据库备份

转载&#xff1a;数据库备份与还原 1. 以下过程为记录客户单位备份过程。 一般有E盘选择E盘备份&#xff0c;否则选择D盘备份。选中备份文件需要重命名&#xff0c;以防原文件被覆盖。切换选项&#xff0c;选中压缩备份。

IoTDB 入门教程 问题篇④——外网攻击导致OOM内存溢出,服务崩溃

文章目录 一、前文二、思考问题三、验证问题五、深入思考六、总结 一、前文 IoTDB入门教程——导读 二、思考问题 从上一篇博客得知&#xff0c;因为内存大小设置的不合理&#xff0c;容易导致OOM内存溢出&#xff0c;最终导致服务崩溃。 事后转念一想&#xff0c;又在思考&…

【ADC】欠采样的基本理论及其应用中的抗混叠滤波器设计原则

概述 之前的两篇文章&#xff1a;信号采样中的频谱混叠现象、频谱混叠的MATLAB仿真与抗混叠滤波器设计 介绍了关于 ADC 采样时&#xff0c;有关奈奎斯特采样定律的基本理论、频谱混叠现象的由来&#xff0c;以及抗混叠滤波器的设计与仿真。但这都是基于过采样的情况&#xff0…

如何理解进程?

前言&#xff1a; 我们在前面的对操作系统的学习&#xff0c;目的是为了让我们加深操作系统对“管理”的描述。我们在上一节了解到操作系统存在许多管理&#xff0c;今天我们就来初次了解一下——进程管理。 进程概念&#xff1a; 课本概念&#xff1a;程序的一个执行实例&…

cube studio 修改logo 水印,标题,图标等信息。

修改logo 修改后需重新打包前端镜像 修改登录界面标题 修改配置文件config.py中的app_name变量&#xff0c;线上需要重启后端pod 修改水印 修改后需重新打包前端镜像 修改标题 修改后需重新打包前端镜像 修改个性化图标 修改后需重新打包后端镜像 修改后需重新打包前端镜…

Elasticsearch:使用 API 密钥验证添加远程集群

API 密钥身份验证使本地集群能够通过跨集群 API 密钥&#xff08;cross-cluster API key&#xff09;向远程集群进行身份验证。API 密钥需要由远程集群的管理员创建。本地集群配置为在向远程集群发出每个请求时提供此 API 密钥。远程集群将根据 API 密钥的权限验证 API 密钥并授…

JVM—CMS收集器

参考资料&#xff1a;深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践&#xff08;第3版&#xff09;周志明 CMS收集器&#xff08;Concurrent Mark Sweep&#xff09;是一种以获取最短回收停顿时间为目标的收集器。低并发收集、低停顿。 CMS收集器是基于标记—清除算法实…

数据库相关概念大全!

一、什么是数据库&#xff1f; 1.数据库 数据库是一种更易于访问、更高效且更有条理的长期存储和处理信息的方式。 2. 优点&#xff0c;作用 数据库存储数据的规范性和系统性以及其检索数据的便捷性使其成为基于 Web 的应用程序中重要的部分。 数据库几乎可以用于所有应用程…

谷粒商城实战笔记-110~114-全文检索-ElasticSearch-查询

文章目录 一&#xff0c;110-全文检索-ElasticSearch-进阶-两种查询方式二&#xff0c;111-全文检索-ElasticSearch-进阶-QueryDSL基本使用&match_all三&#xff0c;112-全文检索-ElasticSearch-进阶-match全文检索四&#xff0c;113-全文检索-ElasticSearch-进阶-match_ph…

HarmonyOS 用List组件实现组合列表项

界面分析&#xff1a; 由于整体UX设计图符合从上至下的布局&#xff0c;所以可以采用Column组件作为外层容器 简介&#xff1a; 最上方的标题可以使用Text组件进行加载渲染中间的Banner图和简介分别可以使用Image组件和Text组件进行加载最后一部分可以看作是一个列表&#xf…