• 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  于宿舍

  • 相关阅读:
    Redhat各个版本和内核对照
    Java8 lambda表达式总结
    conda 安装指定版本的指定包
    git初始化的几句shell
    MYsqli 绑定插入与查询实例
    按天去除重复数据,为0则取0,否则取最大的那个值
    存储过程,循环插入1000条记录
    主表如何统计在附表中的出现次数?
    Invalid argument supplied for foreach()
    二十、mysql mysqldump备份工具
  • 原文地址:https://www.cnblogs.com/CaiBaoZi/p/3120091.html
Copyright © 2020-2023  润新知