1、过滤器的概念
  Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。

  过滤链的好处是,执行过程中任何时候都可以打断,只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。而在实际使用时,就要特别注意过滤链的执行顺序问题。
  
2、过滤器的作用
(1)在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest 。
(2)根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据。
(3)在HttpServletResponse 到达客户端之前,拦截HttpServletResponse 。
(4)根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据。

3、应用举例
3.1、过滤器解决中文乱码的问题,统一全站字符编码的过滤器。
(1)首先定义一个EncodingFiler的实现类,实现Filter接口,代码如下:

public class EncodingFilter implements Filter {
  private FilterConfig config = null;
  private String defaultCharset = "utf-8";

  // Filter被释放时的回调方法
 public void destroy() {

 }

  // FilterConfig接口实例中封装了这个Filter的初始化参数
  public void init(FilterConfig filterConfig) throws ServletException {
      this.config = filterConfig;
  }

  // 过滤方法
 public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
   HttpServletRequest req = (HttpServletRequest) request;
   HttpServletResponse resp = (HttpServletResponse) response;

   String charset = config.getInitParameter("charset");
   if(charset==null){
       charset = defaultCharset;
   }
   request.setCharacterEncoding(charset);

  //有时候response不用设置编码,因为servlet一般不做输出,
 //输出交由jsp来做,所以只要jsp页面设定编码即可
   resp.setCharacterEncoding(charset);
   resp.setContentType("text/html;charset="+charset);

   chain.doFilter(req, resp);
  }
}

(2)在 web.xml 文件中实现Filter实现类,并配置初始化。代码如下:

<?xml version="1.0" encoding="UTF-8"?>
 <web-app version="2.5"
      xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
      <display-name></display-name>

      <filter>
          <filter-name>EncodingFilter</filter-name>
          <filter-class>cn.class3g.web.filter.EncodingFilter</filter-class>
          <init-param>
              <param-name>charset</param-name>
              <param-value>utf-8</param-value>
          </init-param>
      </filter>
</web-app>

3.2、禁止浏览器缓存所有动态页面
(1)有3个HTTP响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:

response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");  
response.setHeader("Pragma","no-cache");

(2)并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。

Expires:值为GMT时间值,为-1指浏览器不要缓存页面。
Cache-Control响应头有两个常用值:
    no-cache:指浏览器不要缓存当前页面。
    max-age:xxx指浏览器缓存页面xxx秒。

(3)实现代码如下:

public class NoCacheFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest req, ServletResponse res,
                FilterChain chain) throws IOException, ServletException {

    HttpServletRequest  request=(HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse) res;

    response.setDateHeader("expires",-1);
    response.setHeader("Cache-Control","no-cache");
    response.setHeader("Pragma","no-cache");

    chain.doFilter(request, response);

    }

    public void destroy() {

    }
}

3.3、控制浏览器缓存页面中的静态资源的过滤器

有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能。

静态资源(Html、Css、Js等资源):静态资源的缓存可以减轻服务器的负担,浏览器一般会默认缓存,为防止用户禁用缓存,在程序代码中进行对静态资源缓存的控制(包括缓存时间的控制)。

(1)过滤器相关配置:

<filter>
    <filter-name>NeedCacheFilter</filter-name>
    <filter-class>cn.itcast.filter.example.NeedCacheFilter</filter-class>
    <init-param>
        <param-name>html</param-name>
        <param-value>1</param-value><!-- 单位是小时 -->
    </init-param>
    <init-param>
        <param-name>css</param-name>
        <param-value>2</param-value><!-- 单位是小时 -->
    </init-param>
    <init-param>
        <param-name>js</param-name>
        <param-value>3</param-value><!-- 单位是小时 -->
    </init-param>
</filter>

<filter-mapping>
    <filter-name>NeedCacheFilter</filter-name>
    <url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>NeedCacheFilter</filter-name>
    <url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>NeedCacheFilter</filter-name>
    <url-pattern>*.js</url-pattern>
</filter-mapping>

(2)filter实现代码

//控制html、css、js等静态资源的缓存时间
public class NeedCacheFilter implements Filter {
    private FilterConfig filterConfig;
    public void destroy() {

    }

    public void doFilter(ServletRequest req, ServletResponse resp,
                    FilterChain chain) throws IOException, ServletException {
        System.out.println("控制缓存时间的过滤器过滤了");
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;
        //获取html、css、js各自的缓存时间,如果没有,给个默认值是1小时
        int time = 1;
        String uri = request.getRequestURI();
        String extendName = uri.substring(uri.lastIndexOf(".")+1);

        if("html".equals(extendName)){
            //访问的html资源
            String value = filterConfig.getInitParameter("html");
            if(value!=null){
                time = Integer.parseInt(value);
            }
        }
        if("css".equals(extendName)){
            //访问的css资源
            String value = filterConfig.getInitParameter("css");
            if(value!=null){
                time = Integer.parseInt(value);
            }
        }
        if("js".equals(extendName)){
            //访问的js资源
            String value = filterConfig.getInitParameter("js");
            if(value!=null){
                time = Integer.parseInt(value);
            }
        }
        response.setDateHeader("Expires", System.currentTimeMillis()+time*60*60*1000);
        chain.doFilter(request, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
}

3.4、日志过滤
使用Filter记录日志、来访IP、URL和页面执行时间。代码如下:

Public final class LoggingFilter implements Filter{
    privateFilterConfigfilterConfig=null;

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

        longstart=System.currentTimeMillis();
        Stringaddress=request.getRemoteAddr();
        Stringfile=((HttpServletRequest)request).getRequestURI();
        chain.doFilter(request,response);

        filterConfig.getServletContext().log(
            "UserAccess!"+
            "UserIP:"+address+
            "Resource:"+file+
            "Millisecondsused:"+(System.currentTimeMillis()-start)
        );
    }

    publicvoiddestroy(){}

    public void init(FilterConfigfilterConfig){
        this.filterConfig=filterConfig;
    }
}

3.5、登录拦截过滤
用户权限的认证,当用户发送请求时,可以对用户的身份信息进行验证,如果能够通过验证则接下来再进行其它操作,否则直接不进入下一步的处理。
(1)web.xml配置

<!-- 配置登陆过滤器 -->
<filter>
    <filter-name>SessionFilter</filter-name>
    <filter-class>com.ccse.frame.filter.SessionFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>SessionFilter</filter-name>
    <url-pattern>/pages/*</url-pattern>
</filter-mapping>

(2)filter代码实现

public class SessionFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {    }

    public void doFilter(ServletRequest request, ServletResponse response,        
                    FilterChain chain) throws IOException, ServletException { 

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        HttpSession session = req.getSession(true);

        // 获得用户请求的URI
        String path = servletRequest.getRequestURI();
        //System.out.println(path);

        // 登陆页面无需过滤
        if(path.indexOf("/login.jsp") > -1) {
             chain.doFilter(servletRequest, servletResponse);
             return;
        }

        //从session里取的用户名信息
        //这句话可以自己修改,看登陆的时候在session里放入什么,就去取什么判断。      
        String username = (String) session.getAttribute("userId");

        //判断如果没有取到用户信息,就跳转到登陆页面
        if ((username == null) || "".equals(username)) {
            //跳转到登陆页面
            res.sendRedirect("http://" + req.getHeader("Host") + "/login.jsp"); 
        } else {
            //已经登陆,继续此次请求 
            chain.doFilter(req, res);
        }
    }

    public void destroy() {    }

}