写点什么

如何优雅的实现一个过滤器

发布于: 2020 年 07 月 23 日
如何优雅的实现一个过滤器

上一篇文章中我们学了了Servlet的概念,这篇我们学习一下过滤器Filter, 正如它的名字,过滤器是介于Servlet之前,可拦截过滤浏览器对于Servlet的请求,也可以改变Servlet对于浏览器的响应。

那要如何实现一个Filter呢,这里我又把Filter的各子类关系图给大家放出来了,从图中可以看出,要想实现一个过滤器,可以直接实现Filter接口,也可以继承他的子类,对于Http请求,则可以直接继承HttpFilter。



从图中可以看出Filter主要有3个要实现的方法: init(), doFilter()和destroy().其中关键的就是这个doFilter()方法,该方法类似于Servlet接口的service()方法。当请求到达容器时,如果容器发现调用Servlet的service()方法前,可以应用某个过滤器, 就会调用该过滤器的doFilter()方法。可以在doFilter()中执行service()的前置处理,并且决定是否调用FilterChain的doFilter()方法。 调用FilterChain的doFilter()方法后,就用运行下一个Filter,如果没有下一个Filter了,就会调用Servlet的service()方法。整理过程会以调用堆栈的顺序返回, 所以在FilterChain的doFiler()方法之后,我们有可以对Servlet的service()方法做后置处理。整个处理过程的图示如下:



接下来我们就来看看如何实现一个过滤器:

方式一:使用@WebFilter注解 和 @ServletComponentScan注解







方式二:使用@Component注解





方式三:使用FilterRegistrationBean







接下来我们来写一个contoller, 来验证一下我的三种方式实现的filter都生效了。





我们调用上面的请求, 会看看再我们的console下出现下面的运行结果,正如我们的预期。



需求一: 指定Filter的运行顺序

如果在程序中想定义多个filter,并且为不同的Filter指定顺序,则只能使用@Component注解和FilterRegistrationBean的方式。具体的使用请参见下图:





需求二: 在Filter中校验失败,抛出异常

在实际项目中,我们通常会定义一个统一的返回信息格式,如果抛出异常,会通过全局统一的异常处理器,使得前端接收到的返回结果都是同一种格式,具体示例如下:

自定义的返回类型

全局异常处理

当我分别在controller和Filter中抛出异常后,得到的结果如下:

Controller中异常处理结果

Filter中异常处理结果



我们会看到在Filter中的异常并没有被我们的全局异常处理器捕捉到,所以也没有被处理成我们想要的统一格式,这是因为Filter并不在我们的Spring上下文中, 要想处理成我们统一定义的格式, 只能我们自己重写,具体示例如下面的代码所示:







不同实现方式的对比

说了这么多那我该用那种方式来实现呢? 这里我总结了几点,供大家参考。

  • 使用@Component方式最简单, 但是这种方式会过滤所有的请求。其余两种方式都可以针对指定的路由进行过滤

  • 使用FilterRegistrationBean的方式实现,无法通过注入的方式获取Spring上下文的其他Bean。

  • 使用@WebFilter的方式实现,无法指定顺序。

spring中提供的Filter

上面介绍了Filter是什么,以及如何实现一个Filter, 不知道大家发现没有,其实我们在实际项目中早就使用了Filter,比如我们常用的跨域处理filter, 具体示例如下所示,其实CorsFilter的本质就是一个Filter, 感兴趣的小伙伴可以去看看源码, 其本质就是实现了Filter接口。



这张图是Spring给我们提供的所有Filter, 我们在自己定义Filter时其实可以通过实现OncePerRequestFilter. 具体它都实现了什么,以及每个Filter都有怎样的用处,我们会在下一篇文章中详细阐述。








发布于: 2020 年 07 月 23 日阅读数: 75
用户头像

废材姑娘 2018.01.24 加入

大家叫我双儿,梦想着成为韦小宝的老婆 欢迎关注我的个人公众号----废材姑娘,回复“双儿”加我微信,让我们一起探索多彩的世界。

评论

发布
暂无评论
如何优雅的实现一个过滤器