当前位置: 首页 > news >正文

【稀里糊涂学Spring MVC】Filter

虽然我们这里介绍的是spring mvc 中的Filter,但是,这些Filter最终都是实现了tomcat中servlet的Filter接口。
下面我们先来看官网api中对Filter的定义

A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.
filter就是request请求在到达资源(resource )之前,可以用来执行一些特定过滤操作的对象。这个资源(resource )可以是一个静态内容,比如页面,图片。也可以是一个servlet。

Filters perform filtering in the doFilter method. Every Filter has access to a FilterConfig object from which it can obtain its initialization parameters, and a reference to the ServletContext which it can use
Filters 都是通过doFilter方法来执行过滤工作的。每个过滤器都可以访问FilterConfig对象,从中可以获得初始化参数,以及对ServletContext的引用

Filters are configured in the deployment descriptor of a web application.
要使用Filter是需要在web应用的deployment descriptor 中进行声明的。所谓deployment descriptor就是我们的web.xml了

看完官网api的解释后,我们再通过一个例子简单解释一下,比如:
你老婆(浏览器)让你(request请求)去幼儿园接儿子(resource)。幼儿园的的传达室的门卫就算是一个filter。他在你接你儿子(resource)之前,他可以先处理你一顿,比如可以先检查你(request请求)身上是不是带了出入证(可以理解为request中的header中的内容,或者request中携带的input中的内容)。如果检查都正常,才你让进去见到你儿子(resource),接他回家。

从例子中可以理解了filter所扮演的角色和作用了吧。在请求(request)资源(resource)的最后一步,获取资源之前,可以通过filter来执行一些操作,比如检查你是否有权限获取这个资源,或者修改reqeust中的一些信息。


那么在spring mvc中,Filter会在执行DispatcherServlet的doDispatch方法之前执行,由此,我们可以通过filter来获取request中的header信息,或者检查一些权限等等的操作。比如springsecurity 和shiro等安全框架,内部其实就是用的filter。

下面先通过一个简单的例子看一下filter的执行时间点,就以我们最常见的CharacterEncodingFilter为例,相信大家都在web.xml配置过。

这个filter的作用就是用来设置request和response的编码的。这样就不用在每个controller的方法中自己处理编码以防止乱码了。
再来看一下filter的执行时间点

通过控制台的栈信息可以看出,是先执行的filter的doFilter方法,然后在进入到DispatcherServlet的doDispatch方法的。
ApplicationFilterChain.doFilter就是负责来执行我们配置的CharacterEncodingFilter。它是servlet container中的东东。

经过上面介绍了一些基础内容后,我们来看一下spring mvc中为我们提供了那些已经可以使用的filter

  • AbstractRequestLoggingFilter
  • CharacterEncodingFilter
  • CommonsRequestLoggingFilter
  • CompositeFilter
  • CorsFilter
  • DelegatingFilterProxy
  • ForwardedHeaderFilter
  • GenericFilterBean
  • HiddenHttpMethodFilter
  • HttpPutFormContentFilter
  • Log4jNestedDiagnosticContextFilter
  • OncePerRequestFilter
  • RelativeRedirectFilter
  • RequestContextFilter
  • ServletContextRequestLoggingFilter
  • ShallowEtagHeaderFilter

下面我来挨个介绍一下这些filter

  1. GenericFilterBean
    Simple base implementation of Filter which treats its config parameters (init-param entries within the filter tag in web.xml) as bean properties.
    是Filter接口实现类的基类。主要用来获取web.xml中配置的filter中设置init-param的的值,然后在将这些值赋值给对配置的filter中的属性

    我们还是以CharacterEncodingFilter为例,来解释一下上面表格中的英文的意思

    上图中init-param中的param-name和param-value并不是直接通过set方法设置给CharacterEncodingFilter类的,通过GenericFilterBean先解析出来,然后通过反射在复制给CharacterEncodingFilter中对应的属性值得。这就是GenericFilterBean的主要作用之一。
    具体的实现,我们来看一下:
    当servlet 容器(tomcat)启动时,tomcat会执行ApplicationFilterConfig,initFilter方法来调用我们在web.xml中配置的filter的init方法,根据多态,子类如果没有这个方法就会调用父类的,最终就会调用GenericFilterBean的init方法

    【1】标识的就是用来获取web.xml中Filter标签中的init-param中的属性的。FilterConfigPropertyValues这个类从名字上就可以看出,是专门用来获取Filter标签中的属性的,下面看一下里面具体的逻辑,如下:

    【2】中的代码是用来将当前的类包装为BeanWrapper,this这里是CharacterEncodingFilter。
    【3】中便是开始把【1】中获取的属性名和值通过反射,调用子类的set方法赋值进子类的对应属性中


    看到了吧,这个pv就是之前在【1】中创建的PropertyValue对象,里面放的就是从web.xml中filter标签的init-param字标签中设置的属性名和值。
    然后便会通过AbstractNestablePropertyAccessor这个类,从PropertyValue中(这里pv变量)获取到name和value的值,然后通过processLocalProperty这个方法,如下:

    通过这个setValue方法,里面便会通过反射来调用this.wrappedObject这个对象中对应属性的set方法进行属性赋值。如下:

    writeMethod就是set方法,这里便是CharacterEncodingFilter.setEncoding,getWrappedInstance()方法获取反射哪个类的方法,这里便是CharacterEncodingFilter,value就是其值了(UTF-8)。
    通过上面的过程,就可以将web.xml中的Filter标签中的init-prarm子标签设置的属性名和值设置到子类,如例子中的CharacterEncodingFilter中的对应的属性中了。就完成了我们所说的属性文件到类的属性的赋值工作。
    这也就解释了表格中描述的关于GenericFilterBean的描述了----主要用来获取web.xml中配置的filter中设置init-param的的值,然后在将这些值赋值给对配置的filter中的属性。

  2. OncePerRequestFilter
    OncePerRequestFilter继承自GenericFilterBean,如下:

    public abstract class OncePerRequestFilter extends GenericFilterBean {
        ...代码省略...
    }

    先来看先api中的说明

    Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container. It provides a doFilterInternal method with HttpServletRequest and HttpServletResponse arguments.
    其意思可以理解为:OnecePerReqeustFilter这个基类,提供了一个doFilterInternal 的方法,所有它的子类都要重写这个方法,然后OnecePerReqeustFilter就可以在doFilter方法中去调用子类重写的doFilterInternal 方法。这样各个子类就可以关注自己filter要处理的业务即可。然后由OnecePerReqeustFilter来执行filter的调用,并且保证每个filter值执行一次。
    大家可以去观察每个子类,这写子类中都没有doFilter方法,只有doFilterInternal 方法。

    下面我们同样使用上面的web.xml来作为例子,来分析一下这个类。
    打开源码会发现,只有这个类才有doFilter方法,甚至连他的基类GenericFilterBean都没有doFilter方法,这说明OncePerRequestFilter才是springmvc中Filter开始的地方,当作为servlet 容器的tomcat执行时在调用doFilter时,会首先进入的就是OncePerRequestFilter实现的Filter接口的doFilter方法,然后通过这个方法内部通过doFilterInternal 方法来调用各个实现具体filter业务的子类(例如CharacterEncodingFilter这个filter具体的任务都是在doFilterInternal 中,而不是自己重写doFilter方法),源码如下;

    【1】处的意思是直接跳到下一个Filter,举例子来说我们有3个类A,B,C都集成了OncePerRequestFilter,如果当前该执行的是A,此时如果执行了【1】处,那么就会直接跳过A,然后进入到B。此时如果程序能跑到【2】处,那么就会调用B里面的doFilterInternal 方法,执行B中的业务逻辑。具体的可以看一下springmvc中各个OncePerRequestFilter的子类。
    上面截图中最主要的就是【2】,这里就是能够调用CharacterEncodingFilter(web.xml中声明的springmvc自带的Filter)等其他Filter的入口。一句话就是,如果你在web.xml中声明了n个springmvc自带的filter或者你自定义的filter,那么每个filter都是通过这里被调用的。
    这其实也就是这个OncePerRequestFilter的主要作用,作为子filter的调用者。它本身并没有什么具体的业务逻辑,业务逻辑都是他的子类去完成的。

相关文章:

  • HK-WEKA如何为勒索软件保护和业务连续性提供支持?
  • springboot+mybaties-plus自动建表
  • 企业IP地址跟踪
  • C++ 小游戏 视频及资料集(3)
  • 十、ThreadPoolExecutor 手撕核心源码
  • Refind多引导系统界面
  • 变身小小科学家 南瓜科学让孩子爱上实验
  • 分布式之ZooKeeper概述
  • app毕业设计开题报告基于Uniapp实现的移动端的医生寻访平台计算机毕业设计
  • 分布式链路追踪技术 Sleuth +Zipkin
  • wsl kali-linux 安装记录
  • SpringBoot整合minio分布式文件实操
  • 电商API按关键字搜索商品
  • 低代码平台类型多,选型记得看看这几点
  • 【Python】第七课 文件存储
  • Babel配置的不完全指南
  • CentOS从零开始部署Nodejs项目
  • extjs4学习之配置
  • HTTP中GET与POST的区别 99%的错误认识
  • HTTP中的ETag在移动客户端的应用
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • maven工程打包jar以及java jar命令的classpath使用
  • Python学习之路13-记分
  • SOFAMosn配置模型
  • spring-boot List转Page
  • Vue 动态创建 component
  • windows-nginx-https-本地配置
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 聚类分析——Kmeans
  • 前端攻城师
  • 前端之React实战:创建跨平台的项目架构
  • 全栈开发——Linux
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 进程与线程(三)——进程/线程间通信
  • 如何正确理解,内页权重高于首页?
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • $.each()与$(selector).each()
  • (1)(1.11) SiK Radio v2(一)
  • (1)(1.13) SiK无线电高级配置(六)
  • (6)设计一个TimeMap
  • (day 12)JavaScript学习笔记(数组3)
  • (poj1.3.2)1791(构造法模拟)
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (力扣)循环队列的实现与详解(C语言)
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (推荐)叮当——中文语音对话机器人
  • (译)2019年前端性能优化清单 — 下篇
  • (转载)深入super,看Python如何解决钻石继承难题
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存