filter修改response学习笔记

前言:这个是我自己学习《Java Web 整合开发 王者归来》的学习笔记,对你们可能没有参考价值。这是filter那一章中,关于内容替换的filterGZIP压缩Filter的学习总结。
        这两个Filter中对response进行了修改,把这两个例子的代码都重新实现之后,弄懂了基本原理,但是也出现了一些困惑。
        大家可以下载《Java Web 整合开发 王者归来》的源代码,查阅filter压缩文件。
内容替换Filter的学习心得
        内容替换的原理是,在Servlet将内容输出到response时,response将内容缓存起来,在Filter中进行替换,然后再输出到客户端浏览器,由于默认的response不能严格地将内容缓存起来,因此需要自定义一个具备缓存功能的response。
        如果response输出的的内容为字符类内容,则会调用getWrite()方法;如果是二进制内容则会调用getOutputStream()方法;
        通过扩展javax.servlet.http.HttpServletResponseWrapper类覆盖其中的getWrite()方法。
package helloJava.respone;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class HttpCharacterResponseWrapper extends HttpServletResponseWrapper
{
    private CharArrayWriter ref_charArrayWriter = new CharArrayWriter();

    public HttpCharacterResponseWrapper(HttpServletResponse response)
    {
        super(response);
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(ref_charArrayWriter);
    }
    public CharArrayWriter getCharArrayWriter() {
        return ref_charArrayWriter;
    }

}
HttpServletResponseWrapper代码

    在Filter中将自定义的response传进Servlet中代码如下:

package helloJava.filter;

import helloJava.respone.HttpCharacterResponseWrapper;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;

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;

public class OutputReplaceFilter implements Filter{

    //属性集合,敏感词集合
    private Properties pp=new Properties();
    @Override
    public void destroy()
    {
        // TODO Auto-generated method stub        
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException
    {
        // TODO Auto-generated method stub
        //自定义的 Response
        HttpCharacterResponseWrapper ref_charResponse=new HttpCharacterResponseWrapper(
                                            (HttpServletResponse)response);
        chain.doFilter(request,ref_charResponse);
        
        String output=ref_charResponse.getCharArrayWriter().toString();
        for(Object obj:pp.keySet())
        {
            String key=(String) obj;
            output=output.replace(key,pp.getProperty(key));
        }
        //这部分不是特别明白,response是入口参数传递过来的
        PrintWriter out =response.getWriter();
        out.write(output);
        out.println("<!--Generated at "+new java.util.Date()+"-->");
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        // TODO Auto-generated method stub
        String file=filterConfig.getInitParameter("file");
        String realPath=filterConfig.getServletContext().getRealPath(file);
        try
        {
            pp.load(new FileInputStream(realPath));
        }
        catch(IOException e)
        {

        }
    }
}
OutputReplaceFilter 代码

    这段代码的主要功能是,在初始化阶段init方法读取敏感词的配置文件doFilter方法根据敏感词去进行替换;

我理解的执行顺序
        1生成自己定义的response带缓存的,
        2然后chain.doFilter(request,ref_charResponse),这部分代表的FilterChain继续执行,当它调用结束时代表response已经是经过服务器处理过之后要返回给客户端浏览器的内容了,ref_charResponse代表服务器返回的处理结果。
        3 由于ref_charResponse是我们自己处理的,它缓存了服务器要返回去的内容,这个时候就可以对缓存中的内容进行内容替换了。
        4利用如果参数传递过来的response,把替换的内容(也就是ref_charResponse的内容)存入到response中,然后返回。
问题:我对第3,4步理解的是否正确
GZIP压缩Filter学习心得
        网站常使用GZIP压缩算法对网页内容进行压缩,然后传给浏览器。首先需要根据Http的Header查看浏览器是否支持GZIP编码方式相关代码如下:
  
....
        HttpServletRequest request=(HttpServletRequest)req;
        HttpServletResponse response=(HttpServletResponse)res;
        
        String acceptEncoding=request.getHeader("Accept-Encoding");
        System.out.println("Accept-Encoding: "+acceptEncoding);
    
        if(acceptEncoding!=null
                &&acceptEncoding.toLowerCase().indexOf("gzip")!=-1){....}
     为了压缩,需要重新定义一个继承自HttpServletResponseWrapper的类,与上一个内容替换Filter定义的response不同的是,这个response不仅处理文本类,还要处理二进制类,因此需要覆盖getOutputStream()和getWriter()方法。处理二进制和文本通过两个类完成GZipResponseWrapperGZipOutputStream;其中GZipOutputStream完成对二进制流的压缩。
        对于二进制内容,当调用GZipOutputStream的close方法时,会通过response的相关方法传递到到客户端浏览器,类似于内容替换Filte的下见源代码。  
package helloJava.gzip;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;


public class GZipOutputStream extends ServletOutputStream
{
    private HttpServletResponse ref_http_response;
    
    private GZIPOutputStream ref_gzipOutputStream;
    
    private ByteArrayOutputStream ref_byteArrayOutputStream;
    
    public GZipOutputStream(HttpServletResponse response)throws IOException
    {
        this.ref_http_response=response;
        ref_byteArrayOutputStream=new ByteArrayOutputStream();
        ref_gzipOutputStream=new GZIPOutputStream(ref_byteArrayOutputStream);
    }
    @Override
    public void write(int b) throws IOException
    {
        // TODO Auto-generated method stub
        ref_gzipOutputStream.write(b);
    }
    public void close()throws IOException
    {
        //压缩完毕一定要调用该方法
        ref_gzipOutputStream.finish();
        //将压缩后的数据输出到客户端
        byte [] content=ref_byteArrayOutputStream.toByteArray();
        
        //设定压缩方式为GZIP,客户端会自动将压缩数据解压
        ref_http_response.addHeader("Content-Encoding", "gzip");
        ref_http_response.addHeader("Content-Length", Integer.toString(content.length));
        
        //输出
        ServletOutputStream out=ref_http_response.getOutputStream();
        out.write(content);
        out.close();
    }
    public void flush()throws IOException
    {
        ref_gzipOutputStream.flush();
    }

}
GZipOutputStream代码
  
package helloJava.gzip;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import helloJava.gzip.GZipOutputStream;

public class GZipResponseWrapper extends HttpServletResponseWrapper
{

    private HttpServletResponse ref_response;
    
    //自定义的outputStream,执行close()的时候对数据压缩,并输出
    private GZipOutputStream ref_gzipOutputStream;
    //自定义printWriter,将内容输出到GZipOutputStream中
    private PrintWriter ref_writer;
    public GZipResponseWrapper(HttpServletResponse response)
    {
        super(response);
        // TODO Auto-generated constructor stub
        this.ref_response=response;
    }
    
    public ServletOutputStream getOutputStream()throws IOException
    {
        if(ref_gzipOutputStream==null)
            ref_gzipOutputStream=new GZipOutputStream(ref_response);
        return ref_gzipOutputStream;
    }
    
    public PrintWriter getWriter() throws IOException {
        if (ref_writer == null)
            ref_writer = new PrintWriter(new OutputStreamWriter(
                    new GZipOutputStream(ref_response), "UTF-8"));
        return ref_writer;
    }
    // 压缩后数据长度会发生变化 因此将该方法内容置空
    public void setContentLength(int contentLength) {
    }
    public void flushBuffer()throws IOException
    {
        ref_gzipOutputStream.flush();
    }
    public void finishResponse()throws IOException
    {
        if(ref_gzipOutputStream!=null)
            ref_gzipOutputStream.close();
        if(ref_writer!=null)
            ref_writer.close();
    }
}
GZipResponseWrapper
  我困惑的是对于文本文件的压缩是怎么完成的
  对于getWriter()的源代码覆盖代码,中有GZipOutputStream部分,结合GZipResponseWrapper中的Close方法代码,我认为文本类的压缩也是通过GZipOutputStream完成的,而且PrintWriter的write方法可以接受byte[]的参数。 
public PrintWriter getWriter() throws IOException {
        if (ref_writer == null)
            ref_writer = new PrintWriter(new OutputStreamWriter(
                    new GZipOutputStream(ref_response), "UTF-8"));
        return ref_writer;
    }

                                                                       菜包子

                                                                   2013年6月5日22:45:00  于宿舍

原文地址:https://www.cnblogs.com/CaiBaoZi/p/3120091.html