过滤器(Filter)的概念

  • 过滤器位于客户端和web应用程序之间,用于检查和修改两者之间流过的请求和响应。
  • 在请求到达Servlet/JSP之前,过滤器截获请求。
  • 在响应送给客户端之前,过滤器截获响应。
  • 多个过滤器形成一个过滤器链,过滤器链中不同过滤器的先后顺序由部署文件web.xml中过滤器映射<filter-mapping>的顺序决定。
  • 最先截获客户端请求的过滤器将最后截获Servlet/JSP的响应信息。

过滤器的链式结构

可以为一个Web应用组件部署多个过滤器,这些过滤器组成一个过滤器链,每个过滤器只执行某个特定的操作或者检查。这样请求在到达被访问的目标之前,需要经过这个过滤器链。

实现过滤器

在Web应用中使用过滤器需要实现javax.servlet.Filter接口,实现Filter接口中所定义的方法,并在web.xml中部署过滤器。

public class MyFilter implements Filter {

public void init(FilterConfig fc) {
        //过滤器初始化代码
    }

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        //在这里可以对客户端请求进行检查
        //沿过滤器链将请求传递到下一个过滤器。
        chain.doFilter(request, response);
        //在这里可以对响应进行处理

}

public void destroy( ) {
        //过滤器被销毁时执行的代码
    }

}

Filter接口

public void init(FilterConfig config)

web容器调用本方法,说明过滤器正被加载到web容器中去。容器只有在实例化过滤器时才会调用该方法一次。容器为这个方法传递一个FilterConfig对象,其中包含与Filter相关的配

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

每当请求和响应经过过滤器链时,容器都要调用一次该方法。需要注意的是过滤器的一个实例可以同时服务于多个请求,特别需要注意线程同步问题,尽量不用或少用实例变量。 在过滤器的doFilter()方法实现中,任何出现在FilterChain的doFilter方法之前地方,request是可用的;在doFilter()方法之后response是可用的。

public void destroy()

容器调用destroy()方法指出将从服务中删除该过滤器。如果过滤器使用了其他资源,需要在这个方法中释放这些资源。

部署过滤器

在Web应用的WEB-INF目录下,找到web.xml文件,在其中添加如下代码来声明Filter。

<filter>
<filter-name>TlwModifyResponseFilter</filter-name>
<filter-class>
com.Common.action.TlwModifyResponseFilter
</filter-class>
</filter>

<filter-mapping>
<filter-name>TlwModifyResponseFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

以上是我的项目工程中的action路径

在2.4版本的servlet规范在部属描述符中新增加了一个<dispatcher>元素,这个元素有四个可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,
可以在一个<filter-mapping>元素中加入任意数目的<dispatcher>,使得filter将会作用于直接从客户端过来的request,通过forward过来的request,通过include过来的request和通过<error-page>过来的request。如果没有指定任何<dispatcher>元素,默认值是REQUEST。

可以通过下面几个例子来辅助理解。   
例1:  
 <filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
</filter-mapping>

这种情况下,过滤器将会作用于直接从客户端发过来的以/products/…开始的请求。因为这里没有制定任何的<dispatcher>元素,默认值是REQUEST。

例2:

  <filter-mapping>
<filter-name>Logging Filter</filter-name>
<servlet-name>ProductServlet</servlet-name>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>

这种情况下,如果请求是通过request   dispatcher的include方法传递过来的对ProductServlet的请求,则要经过这个过滤器的过滤。其它的诸如从客户端直接过来的对ProductServlet的请求等都不需要经过这个过滤器。   
     指定filter的匹配方式有两种方法:直接指定url-pattern和指定servlet,后者相当于把指定的servlet对应的url-pattern作为filter的匹配模式,filter的路径匹配和servlet是一样的,都遵循servlet规范中《SRV.11.2   Specification   of   Mappings》一节的说明  。

例3:

  <filter-mapping>
<filter-name>Logging Filter</filter-name>
<url-pattern>/products/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

在这种情况下,如果请求是以/products/…开头的,并且是通过request   dispatcher的forward方法传递过来或者直接从客户端传递过来的,则必须经过这个过滤器。

1.请求过滤器

web.xml中配置如下

<filter>
<filter-name>MyFilter</filter-name>
<filter-class>cn.telling.Filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern> </filter-mapping>
public class MyFilter implements Filter{
FilterConfig config; /**
*
* @Description: TODO
* @param filterConfig
* @throws ServletException
* @author xingle
* @data 2015-10-26 下午4:32:44
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("begin do the log filter!");
this.config = filterConfig;
} /**
*
* @Description: TODO
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
* @author xingle
* @data 2015-10-26 下午4:32:44
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletContext context = this.config.getServletContext();
System.out.println("before the log filter!");
HttpServletRequest hreq = (HttpServletRequest) request;
System.out.println("Log Filter已经截获到用户的请求的地址:"+hreq.getServletPath() );
// Filter 只是链式处理,请求依然转发到目的地址。
chain.doFilter(request, response);
} /**
*
* @Description: TODO
* @author xingle
* @data 2015-10-26 下午4:32:44
*/
@Override
public void destroy() {
this.config = null;
} }

2.响应过滤器

比如要实现输出压缩:

这样不行!输出会从servlet直接返回给客户。但是我们的目标是压缩输出。

先来想想这样一个问题…… servlet 实际上是从响应对象得到输出流或书写器。那么,如果不把实际的相应对象传给servlet,而是由过滤器换入一个定制的相应对象,而且这个定制响应对象有你能控制的一个输出流,这样可以吗?需要建立我们自己的HttpServletResponse 接口定制实现,并把它通过chain.doFilter() 调用传递到servlet。而且这个定制实现还必须包含一个定制输出流,因为这正是我们的目标,在servlet写输出之后并且在输出返回给客户之前,过滤器就能拿到这个输出。

servlet中使用HttpServletResponseWrapper截获返回的页面内容

要截获页面返回的内容,整体的思路是先把原始返回的页面内容写入到一个字符Writer,然后再组装成字符串并进行分析,最后再返回给客户端。代码如下:

package cn.telling.Filter;

import java.io.CharArrayWriter;
import java.io.PrintWriter; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper; /**
* 自定义一个响应结果包装器,将在这里提供一个基于内存的输出器来存储所有
* 返回给客户端的原始HTML代码。
* @ClassName: ResponseWrapper TODO
* @author xingle
* @date 2015-10-27 上午9:22:14
*/
public class ResponseWrapper extends HttpServletResponseWrapper {
private PrintWriter cachedWriter;
private CharArrayWriter bufferedWriter; /**
* @param response
*/
public ResponseWrapper(HttpServletResponse response) {
super(response);
// 这个是我们保存返回结果的地方
bufferedWriter = new CharArrayWriter();
// 这个是包装PrintWriter的,让所有结果通过这个PrintWriter写入到bufferedWriter中
cachedWriter = new PrintWriter(bufferedWriter);
} public PrintWriter getWriter(){
return cachedWriter;
} /**
* 获取原始的HTML页面内容。
* @return
*/
public String getResult() {
return bufferedWriter.toString();
} }

然后再写一个过滤器来截获内容并处理:

package cn.telling.Filter;

import java.io.IOException;
import java.io.PrintWriter; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse; /**
*
* @ClassName: MyServletFilter TODO
* @author xingle
* @date 2015-10-27 上午9:24:34
*/
public class MyServletFilter implements Filter { /**
*
* @Description: TODO
* @param filterConfig
* @throws ServletException
* @author xingle
* @data 2015-10-27 上午9:24:47
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub } /**
*
* @Description: TODO
* @param request
* @param response
* @param chain
* @throws IOException
* @throws ServletException
* @author xingle
* @data 2015-10-27 上午9:24:47
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 使用我们自定义的响应包装器来包装原始的ServletResponse
ResponseWrapper wrapper = new ResponseWrapper((HttpServletResponse) response);
// 这句话非常重要,注意看到第二个参数是我们的包装器而不是response
chain.doFilter(request, wrapper);
// 处理截获的结果并进行处理,比如替换所有的“名称”为“铁木箱子”
String result = wrapper.getResult();
result = result.replace("名称", "替换后的");
// 输出最终的结果
PrintWriter out = response.getWriter();
out.write(result);
out.flush();
out.close();
} /**
*
* @Description: TODO
* @author xingle
* @data 2015-10-27 上午9:24:47
*/
@Override
public void destroy() {
// TODO Auto-generated method stub } }

然后将该servlet配置在web.xml文件中,如下:

    <filter>
<filter-name>MyFilter</filter-name>
<filter-class>cn.telling.Filter.MyServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern> </filter-mapping>

然后我们在web应用根目录下建立一个jsp文件echo.jsp,内容如下:

<%@page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<title>页面返回结果过滤测试</title></head>
</head>
<body>
你好,我叫“名称”。
</body> </html>

配置完后,部署到tomcat,然后访问应用下的echo.jsp文件,就可以发现返回的内容变成了:

从而也就达到了我们想要的效果了。在文章开头我也提到了说有一个问题,那就是有可能在运行的过程中页面只输出一部分,尤其是在使用多个框架后(比如sitemesh)出现的可能性非常大,在探究了好久之后终于发现原来是响应的ContentLength惹的祸。因为在经过多个过滤器或是框架处理后,很有可能在其他框架中设置了响应的输出内容的长度,导致浏览器只根据得到的长度头来显示部分内容。知道了原因,处理起来就比较方便了,我们在处理结果输出前重置一下ContentLength即可,如下:

// 重置响应输出的内容长度
response.setContentLength(-1);
// 输出最终的结果
PrintWriter out = response.getWriter();
out.write(result);
out.flush();
out.close();

这样处理后就不会再出现只出现部分页面的问题了!

理解Servlet过滤器 (javax.servlet.Filter)的更多相关文章

  1. Eclipse中没有javax.servlet和javax.servlet.http包的处理办法

    使用Eclips开发JSP也需要这两个包:javax.servlet和javax.servlet.http:若提示没有javax.servlet包则安装如下处理办法解决: 如果你装了Tomacat,那 ...

  2. Servlet过滤器,Servlet过滤器创建和配置

    第一:Servlet的过滤器的创建和配置,创建一个过滤器对象需要实现javax.servlet.Filter接口,同时实现Filter的3个方法.        第一方法是过滤器中的init()方法用 ...

  3. 在jsp中如何使用javax.servlet.http.HttpServlet,javax.servlet.GenericServlet, javax.servlet.Servlet

  4. Servlet之javax.servlet包

    链接 : http://blog.sina.com.cn/s/blog_5d4214c70102wnf1.html

  5. javax.servlet.ServletException: javax.servlet.jsp.JspTagException: Invalid property in &lt;set&gt;: "age2"

    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"% ...

  6. servlet过滤器Filter(理论篇)

    为了减少servlet容器在服务器端对信息的判断量,产生了servlet过滤器. servlet过滤器是在java servlet规范2.3中定义的,他能够对servlet容器的请求和响应对象进行检查 ...

  7. java基础篇---Servlet过滤器

    Servlet过滤器从字面上的字意理解为景观一层次的过滤处理才达到使用的要求,而其实Servlet过滤器就是服务器与客户端请求与响应的中间层组件,在实际项目开发中Servlet过滤器主要用于对浏览器的 ...

  8. Servlet过滤器

    Servlet过滤器 [TOC] 1.过滤器的基本概念 1.1.基本概念 过滤器(Filter)属于tomcat服务器中的Servlet功能.在普通的javaweb服务中,jsp中的请求要被Servl ...

  9. Servlet过滤器和监听器知识总结(转)

    Servlet过滤器和监听器知识总结(转)  Servlet过滤器是 Servlet 程序的一种特殊用法,主要用来完成一些通用的操作,如编码的过滤.判断用户的登录状态.过滤器使得Servlet开发者能 ...

随机推荐

  1. 线程入门之优先级priority

    package com.thread; /** * 优先级: * Thread.MAX_PRIORITY:最大优先级 10 * Thread.MIN_PRIORITY:最小优先级 1 * Thread ...

  2. STORM_0008_Structure-of-the-codebase_Storm的代码库的结构

    http://storm.apache.org/releases/1.0.1/Structure-of-the-codebase.html Structure of the codebase 源码分成 ...

  3. 如何设置table中<tr>和<td>的高度

    //-----------------自定义表格table的行和列的宽和高----------------------// 先设置一个样式 如下: <style type="text/ ...

  4. 整理的一些常用的CSS HACK

     ie8以下兼容透明都和支持CSS圆角,这两个都要在服务器上才看到效果,可以本地搭建一个服务器如IIS -pie-background: rgba(255, 255, 255, 0.10);/*IE6 ...

  5. 关于Java控制台输入输出乱码问题

    产生原因:因为这个开源项目的默认字符编码为UTF-8,所以我的控制台的字符编码也自动变成了UTF-8,而键盘的输入流的默认格式是GBK格式,这样就造成了在GBK转UTF-8的过程中产生的奇数乱码错误( ...

  6. mysql概要(四)order by,group 的特点,子查询

    1.order by 默认按升序排列(asc/desc),多字段排序 order by 字段 排序方式,字段2 排序方式,..:在分组排序中,排序是对分组后的结局进行排序,而不是在组中进行排序. 2. ...

  7. 实例化bean的三种方式

    简单的说 当获取bean时: 1.直接创建对象 2.不创建对象,直接调用factory-method指定的静态方法 3.先创建对象,再调用factory-method指点的非静态方法

  8. [转载] Google大数据引擎Dremel剖析(1)

    原文: https://mp.weixin.qq.com/s?__biz=MjM5NzAyNTE0Ng==&mid=207895956&idx=1&sn=58e8af26fd3 ...

  9. [转载] linux 速查表

    原文: http://www.nixtutor.com/linux/all-the-best-linux-cheat-sheets/ 1. Linux Command Line Linux Refer ...

  10. bootstrap学习笔记<六>(表单二之按钮)

    按钮(补充) (ps:居中元素可以使用<center></center>标签) 块级按钮(ps:按钮占一整行) <button class="btn btn-p ...