一 、了解过滤器
什么是过滤器?
- 一种Java组件 ;
- 可被用来截获并处理发送到servlet之前的请求 ;
- 可被用来处理servlet运行完而发送回客户端之前 的响应 ;
- 在DD中配置容器何时调用过滤器。
过滤器有什么作用?
过滤器分为请求过滤器和响应过滤器
- 请求过滤器
- 执行安全检查
- 重新格式化请求的头或体
- 审计或记录请求的日志
- 响应过滤器
- 压缩响应流
- 追加或变更响应流
- 创建一个完全不同的响应
过滤器的特点
过滤器是模块化的
- 可以链式排列
- 完全是自包含的
- 在DD中配置过滤器运行的顺序
过滤器与servlet相似
容器对过滤器API是感知的
过滤器实现Filter接口
过滤器可以获得ServletContext,并能与其他过滤器连接
容器管理过滤器的寿命周期
- Init(), destroy(), doFilter()
过滤器在DD中定义
- 一个web应用可以有多个过滤器
- 一个给定的请求可以使多个过滤器运行
- 过滤器本身并不知道谁要调用它们,以及它的下一个是谁
二 、过滤器的寿命周期
每个过滤器必须实现Filter接口中的三个方法:
- init():容器决定实例化一个过滤器时,该方法可以在过滤器被调用前执行一些构建任务,如获得 FilterConfig对象的引用。
- doFilter():当容器确定该过滤器应当用于当前请求时, 调用此方法。可以在其中执行过滤器功能。有三个参数:
- ServletRequest
- ServletResponse
- FilterChain
- destroy():当容器决定移除一个过滤器实例时,调用 该方法,可以在其中执行一些清除对象的代码
FilterChain接口
过滤器是一些模块化的“构造块”,可以任何顺 序拼凑在一起,FilterChain知道过滤器的拼凑方式,FilterChain接口的doFilter()方法计算下来应调用 哪个过滤器的doFilter()方法,或者servlet的 service()方法,而Filter接口的doFilter()方法执行 实际的过滤操作。
三 、怎么编写一个过滤器
3.1 在DD中配置过滤器
1.声明过滤器
声明过滤器就是在DD中的filter标签中设置:
<filter-name>
过滤器的内部名字,用来找到<filter-mapping>
的内容,这个属性是必须的。<filter-class>
是标记了处理过滤器的java代码所在的位置,也是必须的<init-param>
是初始化参数,这个是可选的,比如下面这个例子初始化了用户登录的文件User.txt。在处理器里面可以获取这个文件,并进行处理。

2. 将过滤器与打算过滤的web资源映射起来
1)定义web资源通
用URL样式定义web资源
1 | <filter-mapping> |
用servlet名字定义web资源
1 | <filter-mapping> |
2)请求转发的web资源
请求转发的web资源,可以有0到4个<dispatcher>
元素。
REQUEST表示过滤器是对客户端请求的,是缺省值;
INCLUDE表示过滤器是对从include()调用的请求转发的;比如对<jsp:include>
引入的页面进行处理。
FORWARD表示过滤器 是对从forward()调用的请求转发的;
ERROR表示过滤器是对错误处理 器调用的资源的
1 | <filter-mapping> |
3) 组织这些映射关系来创建过滤器调用序列
所有需匹配URL样式的过滤器先排序 。
能匹配URL样式的过滤器依照其在DD中声明的顺序放置到FilterChain中。
过滤器链的排序原则:过滤器首先是根据url-pattern来判断是用哪个过滤器,如果url-pattern符合多个过滤器的时候,就根据过滤器在web.xml中配置的先后顺序来依次执行过滤器。然后根据servlet-name来判断过滤器,过滤器的顺序按照DD中声明的顺序来。
3.2 编写过滤器 (请求过滤器)
需求:编写一个请求过滤器,实现对除了首页SelectBeer.jsp之外所有通过get方法访问页面请求的过滤,当通过get方法访问页面时,自动跳转回首页SelectBeer.jsp。如:在浏览器直接访问BeerList.jsp,则自动跳转回首页。
首先需要在DD中配置filter,设置初始化参数 includePage:不被过滤的主页;redirectPath:过滤后跳转的页面。过滤的URL是对这个项目的所有网页进行过滤。
1 | <filter> |
然后编写过滤器
1 | public class GetRequestFilter implements Filter { |
四 、响应过滤器
响应过滤器在servlet执行完后,可以在响应发送到客端前 ,对响应输出做一些处理。
那如何实现这一想法呢?
直接使用容器传来的Response对象是否可行?
如下图直接使用容器传来的Response对象,servlet处理完之后并不知道需要输出过滤,把响应传给客户端输出,过滤器的并没有进行过滤处理。

4.1 包装响应对象
我们可以在过滤器中将原响应对象进行处理,使得自定义的响应对象有一个能被我们控制的输出流, 而后通过chain.doFilter()方法把它传给servlet。
也就是我们把原响应对象进行包装,使得包装之后的response对象有一个重载getOutputStream方法。
怎么使得包装之后的response对象有一个重载getOutputStream方法?
通过实现HttpServletResponse接口,重载getOutputStream方法,返回一个包装了的 ServletOutputStream。

下面通过一个响应过滤器的例子来理解上面的概念。
4.2 请求过滤器实例
需求: 实现对页面上输出文本的大写转化,也即是网页所展示的字母都转化为大写。
1.仍然是在DD中配置
1 | <filter> |
2.编写响应的包装器类
创建一个“假的response”–继承HttpServletResponseWrapper类,重载getOutputStream方法。
1 | public class CharResponseWrapper extends HttpServletResponseWrapper { |
3.编写响应过滤器
在过滤器里面把ServletResponse的相应响应换成之前包装的响应对象。
在filter进行替换内容和实际输出。
1 | public class UpperCaseFilter implements Filter { |
总结
响应过滤器就是在Servlet或者jsp接收到response之前,将response使用一个“假的response”替换,让servlet或者jsp的内容输出到这个“假的response”中,然后再对输出内容进行处理,最后再将整个通过真的response输出给客户端。
