基于SpringMVC的拦截器(Interceptor)和过滤器(Filter)的区别与联系

2023-11-13

一 简介

(1)过滤器:

依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等

关于过滤器的一些用法可以参考我写过的这些文章

  • 继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数:https://www.zifangsky.cn/677.html

  • 在SpringMVC中使用过滤器(Filter)过滤容易引发XSS的危险字符:https://www.zifangsky.cn/683.html

(2)拦截器:

依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理

关于过滤器的一些用法可以参考我写过的这些文章:

  • 在SpringMVC中使用拦截器(interceptor)拦截CSRF攻击(修):https://www.zifangsky.cn/671.html

  • SpringMVC中使用Interceptor+cookie实现在一定天数之内自动登录:https://www.zifangsky.cn/700.html

二 多个过滤器与拦截器的代码执行顺序

如果在一个项目中仅仅只有一个拦截器或者过滤器,那么我相信相对来说理解起来是比较容易的。但是我们是否思考过:如果一个项目中有多个拦截器或者过滤器,那么它们的执行顺序应该是什么样的?或者再复杂点,一个项目中既有多个拦截器,又有多个过滤器,这时它们的执行顺序又是什么样的呢?

下面我将用简单的代码来测试说明:

(1)先定义两个过滤器:

i)过滤器1:

package cn.zifangsky.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
public class TestFilter1 extends OncePerRequestFilter {
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		//在DispatcherServlet之前执行
		system.out.println("############TestFilter1 doFilterInternal executed############");
		filterChain.doFilter(request, response);
		//在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
		System.out.println("############TestFilter1 doFilter after############");
//		try {
//			Thread.sleep(10000);
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
	}
}

ii)过滤器2:

package cn.zifangsky.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
public class TestFilter2 extends OncePerRequestFilter {protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
	System.out.println("############TestFilter2 doFilterInternal executed############");
		filterChain.doFilter(request, response);
		System.out.println("############TestFilter2 doFilter after############");
	}
}

iii)在web.xml中注册这两个过滤器:

	<!-- 自定义过滤器:testFilter1 -->	
 	<filter>
		<filter-name>testFilter1</filter-name>
		<filter-class>cn.zifangsky.filter.TestFilter1</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>testFilter1</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!-- 自定义过滤器:testFilter2 -->	
 	<filter>
		<filter-name>testFilter2</filter-name>
		<filter-class>cn.zifangsky.filter.TestFilter2</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>testFilter2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

(2)再定义两个拦截器:

i)拦截器1,基本拦截器:

package cn.zifangsky.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class BaseInterceptor implements HandlerInterceptor{
	/**
	 * 在DispatcherServlet之前执行
	 * */
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
	System.out.println("************BaseInterceptor preHandle executed**********");
		return true;
	}
	/**
	 * 在controller执行之后的DispatcherServlet之后执行
	 * */
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
	System.out.println("************BaseInterceptor postHandle executed**********");
	}
	/**
	 * 在页面渲染完成返回给客户端之前执行
	 * */
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("************BaseInterceptor afterCompletion executed**********");
//		Thread.sleep(10000);
	}
}

ii)指定controller请求的拦截器:

package cn.zifangsky.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class TestInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
		System.out.println("************TestInterceptor preHandle executed**********");
		return true;
	}public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		System.out.println("************TestInterceptor postHandle executed**********");
	}
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("************TestInterceptor afterCompletion executed**********");
	}
}

iii)在SpringMVC的配置文件中注册这两个拦截器:

	<!-- 拦截器 -->
 	<mvc:interceptors>
		<!-- 对所有请求都拦截,公共拦截器可以有多个 -->
		<bean name="baseInterceptor" class="cn.zifangsky.interceptor.BaseInterceptor" />
		<!-- <bean name="testInterceptor" class="cn.zifangsky.interceptor.TestInterceptor" /> -->
		<mvc:interceptor>		
			<!-- 对/test.html进行拦截 -->
			<mvc:mapping path="/test.html"/>
			<!-- 特定请求的拦截器只能有一个 -->
			<bean class="cn.zifangsky.interceptor.TestInterceptor" />
		</mvc:interceptor>
	</mvc:interceptors>

(3)定义一个测试使用的controller:

package cn.zifangsky.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestController {
	@RequestMapping("/test.html")
	public ModelAndView handleRequest(){
		System.out.println("---------TestController executed--------");
		return new ModelAndView("test");
	}
}

(4)视图页面test.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>    
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<base href="http://983836259.blog.51cto.com/7311475/">
<title>FilterDemo</title>
</head>
<body>
	<%
		System.out.println("test.jsp is loading");
	%>
	<div align="center">
		This is test page
	</div>
</body>
</html>

(5)测试效果:

启动此测试项目,可以看到控制台中输出如下:

wKioL1hHhYrRCpQYAABafsYR7go378.png

这就说明了过滤器的运行是依赖于servlet容器的,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关

接着清空控制台中的输出内容并访问:http://localhost:9180/FilterDemo/test.html

可以看到,此时的控制台输出结果如下:

wKiom1hHhaPRQuBxAACG4WdOJbM758.png

相信从这个打印输出,大家就可以很清晰地看到有多个拦截器和过滤器存在时的整个执行顺序了。当然,对于过个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关

注:对于整个SpringMVC的执行流程来说,如果加上上面的拦截器和过滤器,其最终的执行流程就如下图所示:

wKiom1hHhbmxseDtAACidU9Y84s787.png

PS:这个图是我用画图软件画出来的,将就着看吧^_^

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于SpringMVC的拦截器(Interceptor)和过滤器(Filter)的区别与联系 的相关文章

随机推荐

  • 那些会阻碍程序员成长的细节[3]

    前两篇文间几乎是想到那里就写到那里 没有分门别类的加以阐述 本篇延续以上两篇文章的思路 在之前的基础再追加 没有看过前两篇文章的同学可通过这两个链接回顾一下 那些会阻碍程序员成长的细节 一 那些会阻碍程序员成长的细节 二 不能主动推动事物前
  • Halcon标定板标定

    halcon标定有自己的标定助手可以演示 不过拿到VS里面却不是很适用 尤其是关于畸变矫正和透视矫正算子的解释也没有 下面两个算子set origin pose gen image to world plane map关键参数怎么计算也没有
  • 【MyBatis-Plus】之批量插入

    一 应用情景介绍 在实际的项目开发过程中 常常遇到批量保存数据的场景 当数据量比较少 比如只有几条数据的情况下 我们可以使用 for 循环来 insert 数据 但如果数据量比较多的情况下就不行 特别是并发的情况下 因为这样会增加数据库的负
  • 一个学习编程的网站

    推荐一个学习编程的网站 https www runoob com
  • 请假流程

    作者 nogocn 在某一公司中 部门员工要休假的话需要部门主管的批准 如果休假天数大于10天的话 在部门主管的同意后 还必须上级主管批准 如果是部门主管要休假只要上级主管批准即可 在休假被批准之前 申请人可以撤销休假申请 每个员工还有多少
  • STM32的功耗模式

    按功耗由高到低排列 STM32 具有运行 睡眠 停止和待机四种工作模式 低功耗各模式下芯片工作情况 睡眠模式 仅关闭了内核时钟 内核停止运行 但其片上外设 CM4 核心的外设全都还照常 运行 有两种方式进入睡眠模式 它的进入方式决定了从睡眠
  • Java实现数据脱敏的方法

    在Java中 可以使用各种技术来实现数据脱敏 下面将介绍几种常见的Java实现数据脱敏的方法 字符串截取 字符串截取是一种简单的数据脱敏方法 它将敏感数据的一部分字符替换成 号或其他字符 例如 将身份证号码的前6位和后4位替换成 号 这样可
  • 开发一个文生图的功能

    文章目录 效果 开发环境 原理 核心代码 代码仓库 问题 效果 开发环境 Python 3 10 PyCharm 原理 借助开源项目stable diffusion 通过该项目封装python库diffusers 可以轻易的实现文生图的功能
  • css 强制不换行

    css中强制不换行 文本不会换行 文本会在在同一行上继续 直到遇到 br 标签为止 white space nowrap
  • LeetCode-剑指 Offer II 114. 外星文字典,BFS 搜索算法及图的表示

    剑指 Offer II 114 外星文字典 现有一种使用英语字母的外星文语言 这门语言的字母顺序与英语顺序不同 给定一个字符串列表 words 作为这门语言的词典 words 中的字符串已经 按这门新语言的字母顺序进行了排序 请你根据该词典
  • P25透明屏:探究在商业广告领域的应用表现

    P25透明屏是一种新型的显示屏技术 具有高透明度和高分辨率的特点 它可以将图像或视频直接投影到透明的表面上 使得观众可以透过屏幕看到背后的景物 同时也能够看到屏幕上的内容 P25透明屏广泛应用于商业展示 户外广告 产品展示等领域 P25透明
  • 写代码的心得

    一 初级阶段 管他三七二十一 需求直接开干 程序能跑通能干活就行 二 中级阶段 拿到需求心里过一遍 80 的架构和接口已经梳理定义完毕 尚有部分细节的没想明白怎么实现 边开发边想 边写边重构 等开发完 架构也定义清晰 三 高级阶段 拿到需求
  • 编写出一个通用的人员类(Person),该类具有姓名(Name)、年龄(Age)、性别(Sex)等域。然后对Person 类的继承得到一个学生类(Student),该类能够存放学生的5门课的成绩,并能

    编写出一个通用的人员类 Person 该类具有姓名 Name 年龄 Age 性别 Sex 等域 然后对Person 类的继承得到一个学生类 Student 该类能够存放学生的5门课的成绩 并能求出平均成绩 最后在Main函数中对studen
  • vue点击当前元素添加class 删除兄弟元素的class

    在vue中当我们要实现点击元素动态添加类名时 我们不能像JQ那样去用 xxx addClass class siblings removeClass class 实现 那我们应该怎样去做呢 解决方案 1 在data里面申明一个属性 默认值最
  • LeetCode-1780. 判断一个数字是否可以表示成三的幂的和【数学】

    LeetCode 1780 判断一个数字是否可以表示成三的幂的和 数学 题目描述 解题思路一 将n转为3进制 如果没有2出现那么返回true 例如12 110 3 返回true 21 210 3 返回false 解题思路二 0 解题思路三
  • 计算机毕业设计-基于SSM的个性影片推荐管理系统

    项目系统开发的技术 Java语言 Java主要采用CORBA技术和安全模型 可以在互联网应用的数据保护 它还提供了对EJB Enterprise JavaBeans 的全面支持 java servlet API Java java serv
  • 重磅发布

    微众银行区块链积极听取社区意见和建议 基于多年研发FISCO BCOS底层开源平台的经验 沉淀了一套 FISCO BCOS应用落地指南 百科式地为FISCO BCOS的应用落地提供操作指引 现将该指南开放回馈社区 获取指南 可在文末添加 小
  • HashMap和LinkedHashMap的区别

    java为数据结构中的映射定义了一个接口java util Map 它有四个实现类 分别是 HashMap Hashtable LinkedHashMap TreeMap Map主要用于存储健值对 根据键得到值 因此不允许键重复 重复了覆盖
  • 用Flutter构建漂亮的UI界面 - 基础组件篇

    1 前言 Flutter作为时下最流行的技术之一 凭借其出色的性能以及抹平多端的差异优势 早已引起大批技术爱好者的关注 甚至一些闲鱼 美团 腾讯等大公司均已开始使用 虽然目前其生态还没有完全成熟 但身靠背后的Google加持 其发展速度已经
  • 基于SpringMVC的拦截器(Interceptor)和过滤器(Filter)的区别与联系

    一 简介 1 过滤器 依赖于servlet容器 在实现上基于函数回调 可以对几乎所有请求进行过滤 但是缺点是一个过滤器实例只能在容器初始化时调用一次 使用过滤器的目的是用来做一些过滤操作 获取我们想要获取的数据 比如 在过滤器中修改字符编码