过滤器VS拦截器的4个区别,看完豁然开朗!

Spring的拦截器与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。但它们之间又有很大区别,所以本文磊哥就带大家全面了解一下什么是过滤器?什么是拦截器?以及二者有什么区别?

在开始聊二者的区别之前,先来分别认识一下它们。

什么是过滤器(Filter)?

过滤器(Filter)属于Servlet的范畴,可以认为是Servlet的一种“加强版”,通过实现javax.servlet.Filter接口来实现功能。主要用于对用户请求进行预处理,是个典型的处理链。通常使用场景:检查用户授权、记录日志信息、解码、过滤字符编码等。

基本工作原理:配置完过滤器及需要拦截的请求,当请求到来时,通过过滤器提供的方法可以对请求或响应(Request、Response)统一处理。比如,可判断用户是否登录,是否拥有请求的访问权限等。在Web应用启动时,过滤器仅会被初始化一次,便可处理后续请求,只有Web应用停止或重新部署时才能销毁。

使用Filter完整的流程是:Filter对用户请求进行“预处理”,接着将请求交给Servlet进处理并生成响应,最后Filter再对服务器响应进行“后处理”。

上述流程具体到类的处理就是:1、Filter在ServletRequest到达Servlet之前,拦截客户的ServletRequest;2、根据需要检查ServletRequest,也可修改ServletRequest头和数据;3、在ServletResponse到达客户端之前,拦截ServletResponse;4、根据需要检查HttpServletResponse,也可修改HttpServletResponse头和数据。

创建Filter必须实现javax.servlet.Filter接口,该接口定义了3个方法:

  • void init(FilterConfig filterConfig):容器启动初始化Filter时会被调用,整个生命周期只会被调用一次。可用于完成Filter的初始化。

  • void doFilter(ServletRequest request, ServletResponse response,FilterChain chain):实现过滤功能,就是通过该方法对每个请求增加额外的处理。通过其参数FilterChain调用下一个过滤器。

  • void destroy():用于Filter销毁前,完成某些资源的回收。

其中,doFilter方法便是实现对用户请求进行预处理(ServletRequest request)和对服务器响应进行后处理(ServletResponse response)的方法。预处理和后处理的分界线为是否调用了chain.doFilter()。在执行该方法之前,是对用户请求进行预处理,在执行该方法之后,是对服务器响应进行后处理。

下面以具体的实现代码来展示一下:

public class LogFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("Filter 初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("Filter 预处理");filterChain.doFilter(servletRequest, servletResponse);System.out.println("Filter 后处理");}@Overridepublic void destroy() {System.out.println("容器销毁");}
}

关于Filter的使用在普通的Web项目中可在web.xml中配置:

<filter><filter-name>encodingFilter</filter-name><filter-class>com.secbro2.learn.filter.LogFilter</filter-class><async-supported>true</async-supported><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
</filter>
<filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

如果是SpringBoot项目,首先使用@Component将LogFilter实例化,然后通过如下配置文件,进行具体的配置:

@Configuration
public class FilterConfig {@Resourceprivate LogFilter logFilter;@Beanpublic FilterRegistrationBean<Filter> registerAuthFilter() {FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();registration.setFilter(logFilter);registration.addUrlPatterns("/*");registration.setName("authFilter");// 值越小,Filter越靠前registration.setOrder(1);return registration;}
}

定义一个Contoller,然后依次执行启动项目、访问Controller、关闭项目,打印的日志信息依次为:

Filter 初始化
---以上为启动项目时打印---
Filter 预处理
Controller中处理业务逻辑
Filter 后处理
---以上为访问Controller时打印---
容器销毁
---以上为关闭服务时打印---

什么是拦截器(Interceptor)?

拦截器,在AOP(Aspect-Oriented Programming)中用于某个方法或字段被访问之前进行拦截,然后在其之前或之后加入某些操作。拦截器作为动态拦截Action调用的对象,它提供了一种机制使开发者可以在Action执行前后定义可执行的代码,也可以在Action执行前阻止其执行。

拦截器将Action共用的行为独立出来,在Action执行前后执行。常见的应用场景比如权限管理、日志服务等。

在Spring MVC当中要使用拦截器需要实现org.springframework.web.servlet.HandlerInterceptor接口,该接口定义了如下三个方法:

(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,会在请求处理之前被调用。SpringMVC中的Interceptor是链式调用的,可以存在多个Interceptor。Interceptor的调用会依据声明顺序依次执行,最先执行的都是preHandle方法,可在该方法中进行一些前置(预)处理,也可进行判断来决定是否要继续执行。当返回为false 时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时,会继续调用下一个Interceptor的preHandle方法,执行完最后一个Interceptor后会调用当前请求的Controller方法。

(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,会在Controller方法调用之后,DispatcherServlet进行渲染视图之前被调用,所以可以对Controller处理之后的ModelAndView对象进行操作。postHandle方法被调用的方向跟preHandle是相反的,先声明的Interceptor的postHandle方法反而会后执行。

(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,会在整个请求结束之后被调用,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要是用于进行资源清理。

来看一个具体的示例:

@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {System.out.println("Interceptor preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {System.out.println("Interceptor postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {System.out.println("Interceptor afterCompletion");}
}

对应LoginInterceptor需添加到Spring MVC当中:

@Configuration
public class WebConfig implements WebMvcConfigurer {@Resourceprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**");}
}

这里拦截所有的请求,执行对应的Controller之后,会看到打印如下信息:

Interceptor preHandle
Controller中处理业务逻辑
Interceptor postHandle
Interceptor afterCompletion

很明显可以看到,当一个请求过来之后,会先后执行preHandle方法、Controller中的业务、postHandle方法和afterCompletion方法。

过滤器 VS 拦截器

经过上面的学习,我们已经大概了解了过滤器和拦截器的基本使用和功能,想必已经感觉到它们之间的一些区别了。先看一张图,可以更加明显的看出过滤器和拦截器在使用过程中所处的位置和使用的时机。

image

二者的区别如下

1、使用范围与规范不同:Filter是Servlet规范中定义的,只能用于Web程序中,依赖于Servlet容器。拦截器是Spring的组件,可用于Web程序、Application、Swing等程序,不依赖Servlet容器。

2、使用资源不同:拦截器可以使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IOC注入到拦截器即可;而Filter则不能。

3、作用范围不同:Filter在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后,对Action请求其作用,可以访问Action上下文、值栈里的对象等,具有更大的弹性。因此,在Spring框架的过程中,要优先使用拦截器。而滤器则可以对几乎所有的请求起作用。

4、实现机制不同:拦截器是基于java的反射机制的,而过滤器是基于函数回调。

上面介绍了过滤器和拦截器的基本不同之处,这里再对上面的图进一步细化,可得到下图:

通过上图,我们可以进一步看到拦截器和过滤器的方法在整个请求过程中所处的位置。

小结

通过上面的学习,想必大家已经掌握了过滤器和拦截器的基本使用。最后补充一下,什么时候适合使用过滤器,什么时候又适合使用拦截器呢?当需要过滤掉其中的部分信息,只留一部分时,就用过滤器;当需要对其流程进行更改,做相关的记录时用拦截器。


往期推荐

爱了!蚂蚁开源的“SpringBoot”框架,新增了这6项功能...


Docker部署SpringBoot的两种方法,后一种一键部署超好用!


一文汇总 JDK 5 到 JDK 15 中的牛逼功能!


关注我,每天陪你进步一点点!

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

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

相关文章

js 自己试验 创建对象例子

js 创建对象例子 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><script>function login() {alert(this.username " - login");}function CreateUser(username, password) { // var us…

云计算应用迁移_应用迁移策略到云

云计算应用迁移Rehost: 重新托管&#xff1a; Rehosting an application to the cloud without making changes to its architecture or code. 将应用程序重新托管到云&#xff0c;而无需更改其架构或代码。 Refactor: 重构&#xff1a; Involves application code and conf…

看美文,记单词(6)

combat vt.反对&#xff0c;与..战斗 vi.战斗&#xff0c;搏斗 n. 战争&#xff0c;争论 adj. 战斗的.. intensify vi. 增强&#xff0c;强化 vt. 使加强&#xff0c;使强化 detention n. 拘留&#xff0c;挽留&#xff0c;延迟 administrative detention 行政…

分布式ID生成的9种方法,特好用!

前言业务量小于500W或数据容量小于2G的时候单独一个mysql即可提供服务&#xff0c;再大点的时候就进行读写分离也可以应付过来。但当主从同步也扛不住的是就需要分表分库了&#xff0c;但分库分表后需要有一个唯一ID来标识一条数据&#xff0c;数据库的自增ID显然不能满足需求&…

js对象的定义方法

转载&#xff1a;http://blog.sina.com.cn/s/blog_60f632050100wz7h.html &#xff08;1&#xff09;基于已有对象的扩充方法&#xff1a;适用于临时构建对象&#xff0c;弊端&#xff1a;每次构建对象都要新建一个。 var objectnew Object();object.name"Tom";objec…

8051 管脚定义_8051微控制器的引脚说明

8051 管脚定义8051微控制器的引脚说明 (Pin Description of 8051 Microcontroller) Pins from 1-8 1-8针 Port 1: The pins in this port are bi-directional and can be used for input and output. The pins are individually controlled; some are used for input while ot…

android 事件分发

2019独角兽企业重金招聘Python工程师标准>>> 文章来源于CSDN http://blog.csdn.net/lanhuzi9999/article/details/26515421 转载于:https://my.oschina.net/lhjtianji/blog/398998

对象复制的7种方法,还是Spring的最好用!

日常编程中&#xff0c;我们会经常会碰到对象属性复制的场景&#xff0c;就比如下面这样一个常见的三层 MVC 架构。当我们在上面的架构下编程时&#xff0c;我们通常需要经历对象转化&#xff0c;将业务请求流程经历三层机构后需要把 DTO 转为DO然后在数据库中保存。当需要从数…

js visibility和display区别(附代码实例)

<html><head><meta charset"utf-8" /><title></title><script type"text/javascript">//元素的隐藏与显示//visibility属性是隐藏元素但保持元素的浮动位置&#xff0c;而display实际上是设置元素的浮动特征。//PS&am…

Java中的Switch都支持String了,为什么不支持long?

来源 | jitwxs.cn/6f3eddff.html我们知道 Java Switch 支持byte、short、int 类型&#xff0c;在 JDK 1.5 时&#xff0c;支持了枚举类型&#xff0c;在 JDK 1.7 时&#xff0c;又支持了 String类型。那么它为什么就不能支持 long 类型呢&#xff0c;明明它跟 byte、short、int…

什么是WebSocket,以及如何在Python中使用它?

什么是WebSocket&#xff1f; (What is WebSocket?) WebSocket is a communications protocol which provides a full-duplex communication channel over a single TCP connection. WebSocket protocol is standardized by the IETF as RFC 6455. WebSocket是一种通信协议&am…

QCon讲师对对碰——洪小军采访梁宇鹏:就是爱Golang

编者按&#xff1a;QCon北京2015将于4月23日~25日在北京国际会议中心召开。在大会开始之前&#xff0c;InfoQ推出了讲师对对碰栏目&#xff0c;邀请一些技术专家相互采访&#xff0c;碰撞出思维的火花。在QCon上&#xff0c;美图架构平台部门负责人洪小军将分享《美拍后端技术演…

网页之错误代码大全

winray整理~~~ 400 无法解析此请求。 401.1 未经授权&#xff1a;访问由于凭据无效被拒绝。 401.2 未经授权: 访问由于服务器配置倾向使用替代身份验证方法而被拒绝。 401.3 未经授权&#xff1a;访问由于 ACL 对所请求资源的设置被拒绝。 401.4 未经授权&#xff1a;W…

我们如何在Python中创建多行注释?

When we need to comment on multiple lines/statements, there are two ways to do this, either comment each line or create multiline comments (or block comment). 当我们需要对多个行/语句进行注释时&#xff0c;有两种方法可以执行此操作&#xff0c;或者注释每行或创…

final的8个小细节,听说只有高手才知道!你知道几个?

final关键字是一个常用的关键字&#xff0c;可以修饰变量、方法、类&#xff0c;用来表示它修饰的类、方法和变量不可改变&#xff0c;下面就聊一下使用 final 关键字的一些小细节。细节一、final 修饰类成员变量和实例成员变量的赋值时机对于类变量&#xff1a;声明变量的时候…

java实现的简单程序登录界面

2019独角兽企业重金招聘Python工程师标准>>> 这是我写的简单代码&#xff1a; 简单&#xff0c;没什么嚼头&#xff0c;作业贴&#xff0c;直接上代码。文件保存用户名和密码&#xff0c;输入密码错误3次退出程序。 [java] view plaincopy 01.public Login() throws…

js对浏览器内部及外部窗口宽度及高度使用测试

<!DOCTYPE html> <html><head><meta charset"utf-8" /></head><body><p id"demo"></p><script>//Part I 取浏览器内部窗口宽度及高度//该例显示浏览器窗口的高度和宽度&#xff1a;&#xff08;不包…

golang rsa密钥_如何在Golang的地图中检查密钥是否存在?

golang rsa密钥When you try to get the value of key in a map, you get two return values. The first value is the value of key and second is bool value which could be either true or false. If a key is present in the map then the second value will be true else…

try-catch-finally中的4个巨坑,老程序员也搞不定!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Java 语言中 try-catch-finally 看似简单&#xff0c;一副人畜无害的样子&#xff0c;但想要真正的“掌控”它&#xff0…

CentOS7安装Python3.4 ,让Python2和3共存

为什么80%的码农都做不了架构师&#xff1f;>>> #CentOS7安装Python3.4 &#xff0c;让Python2和3共存 环境&#xff1a;CentOS7.1 需求&#xff1a;网络畅通 编译需要的一些包&#xff0c;酌情安装 yum groupinstall "Development tools" yum install z…