12_过滤器

每篇一句:任何普通人都可能随时被委以重任,任何显要人物也可能随时被取代。  ——《三体 III : 死神永生》

过滤器概述

过滤器就是 Java 组件,请求发送到 servlet 之前,可以用过滤器截获和处理请求,在 servlet 结束之后,但在响应发回给客户之前,可以用过滤器处理响应

过滤器的作用

  • 请求过滤器
    • 完成安全检查
    • 重新格式请求首部或体
    • 建立请求审计或日志
  • 响应过滤器
    • 压缩响应流
    • 追加或修改响应流
    • 创建或一个完全不同的响应

实例

public class BeerRequestFilter implements Filter {
    private FilterConfig fConfig;
    //必须实现 init() ,通常只需保存配置对象
    public void init(FilterConfig fConfig) throws ServletException {
        this.fConfig = fConfig;
    }
    //doFilter() 中才做具体的工作
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String name = httpServletRequest.getRemoteUser();
        if(name != null) {
            fConfig.getServletContext().log("User" + name + "is updating");
        }
        //接下来调用的 过滤器 或 servlet
        chain.doFilter(request, response);
    }
    public void destroy() {
        //实现清理工作
    }
}

过滤器的生命周期

每个过滤器都必须实现 Filter 接口中的三个方法:

1.init()

容器决定实例化一个过滤器时,在 init() 方法中完成调用过滤器之前的所有初始化任务

2.doFilter()
过滤器的功能在 doFilter() 方法中实现。doFilter() 方法有三个参数:

  • ServletRequest
  • ServletResponse
  • FilterChain

3.dostroy()

在真正撤销实例之前完成所需的所有清理工作

过滤器的调用

在实际考虑过滤器相互链接的过程时,可以想象成栈上的方法调用

12_过滤器_第1张图片

声明和确定过滤顺序

声明过滤器

<filter>
    <filter-name>BeerReuqestfilter-name>
    <filter-class>com.example.web.BeerRequestFilterfilter-class>
    param>
      <param-name>LogFileNameparam-name>
      <param-value>UserLog.txtparam-value>
    param>
  filter>

声明对应 URL 模式的过滤器映射

元素定义了哪些 Web 应用资源要使用这个过滤器

<filter-mapping>
    <filter-name>BeerReuqestfilter-name>
    <servlet-name>FilterServletservlet-name>
filter-mapping>

声明对应 Servlet 名的过滤器映射

元素定义了哪个 Web 应用资源要使用这个过滤器

<filter-mapping>
    <filter-name>BeerReuqestfilter-name>
    <servlet-name>FilterServletservlet-name>
filter-mapping>

当多个映射器映射到一个给定规则时,容器会使用一下规则:

  • 先找到与 URL 模式匹配的所有过滤器
  • 容器会用同样的方法确定与 DD 中 匹配的过滤器

用于请求分派器

<filter-mapping>
    <filter-name>MonitorFilterfilter-name>
    <url-pattern>*.dourl-pattern>
    <dispatcher>REQUEST<dispatcher>
filter-mapping>

有四个值:

  • REQUEST 值表示对客户请求启用过滤器。默认为 REQUEST
  • INCLUDE 值表示对由一个 include() 调用分派来的请求启用分派器
  • FORWARD 值表示对由一个 forward() 调用分派来的请求启用过滤器
  • ERROR 值表示对错误处理器调用的资源启用过滤器

响应端过滤器压缩输出

如果想创建定制请求或响应对象,只需要派生某个便利请求或响应 “包装器” 类。Sun 提供了 4 个便利类,以创建特定版本的请求或响应

  • ServletRequestWrapper
  • HttpServletRequestWrapper
  • ServletResponseWrapper
  • HttpServletResponseWrapper

包装器包装了实际的请求或响应对象,而且调用委托给实际的对象,还允许对定制请求或响应做所需的额外处理

实例

压缩过滤器代码

public class CompressionFilter implements Filter {
    private FilterConfig fConfig;
    private ServletContext cServletContext;
    public void init(FilterConfig fConfig) throws ServletException {
        this.fConfig = fConfig;
        cServletContext = fConfig.getServletContext();
        cServletContext.log(fConfig.getFilterName() + " initailized.");
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String valid_encodings = httpServletRequest.getHeader("Accept-Encoding");
        if(valid_encodings.indexOf("gzip") > -1) {
            CompressionResponseWrapper wrappedResp = new CompressionResponseWrapper(httpServletResponse);
            wrappedResp.setHeader("Content-Encoding", "gzip");
            chain.doFilter(request, wrappedResp);
            GZIPOutputStream gzipServletOutputStream = wrappedResp.getGZIPOutputStream();
            gzipServletOutputStream.finish();
            cServletContext.log(fConfig.getFilterName() + ":finished the request");
        }
        else {
            cServletContext.log(fConfig.getFilterName() + ": no encoding preformed.");
            chain.doFilter(request, response);
        }
    }
    public void destroy() {
        fConfig = null;
        cServletContext = null;
    }
}

压缩包装器代码

public class CompressionResponseWrapper extends HttpServletResponseWrapper {
    private GZIPServletOutputStream servletGzipOS = null;
    private PrintWriter printWriter = null;
    public CompressionResponseWrapper(HttpServletResponse response) {
        super(response);
    }
    @Override
    public void setContentLength(int length) { }
    public GZIPOutputStream getGZIPOutputStream() {
        return this.servletGzipOS.internalGzipOS;
    }
    private Object streamUsed = null;
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if((streamUsed != null) && (streamUsed != printWriter)) {
            throw new IllegalStateException();
        }
        if( servletGzipOS == null) {
            servletGzipOS = new GZIPServletOutputStream(getResponse().getOutputStream());
            streamUsed = servletGzipOS;
        }
        return servletGzipOS;
    }
    @Override
    public PrintWriter getWriter() throws IOException {
        if( (streamUsed != null) && (streamUsed != servletGzipOS)) {
            throw new IllegalStateException();
        }
        if(printWriter == null) {
            servletGzipOS = new GZIPServletOutputStream(getResponse().getOutputStream());
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(servletGzipOS, getResponse().getCharacterEncoding());
            printWriter = new PrintWriter(outputStreamWriter);
            streamUsed = printWriter;
        }
        return printWriter;
    }
}

辅助类代码

public class GZIPServletOutputStream extends ServletOutputStream {
    GZIPOutputStream internalGzipOS;
    public GZIPServletOutputStream(ServletOutputStream sos) throws IOException {
        this.internalGzipOS = new GZIPOutputStream(sos);
    }
    public void write(int param) throws java.io.IOException {
        internalGzipOS.write(param);
    }
    @Override
    public boolean isReady() { return false; }
    @Override
    public void setWriteListener(WriteListener arg0) { }
}

写在最后

如果有任何问题或建议,欢迎交流学习。

你可能感兴趣的:(JavaWeb)