前言:这个是我自己学习《Java Web 整合开发 王者归来》的学习笔记,对你们可能没有参考价值。这是filter那一章中,关于内容替换的filter和GZIP压缩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; } }
在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) { } } }
这段代码的主要功能是,在初始化阶段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()方法。处理二进制和文本通过两个类完成GZipResponseWrapper和GZipOutputStream;其中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(); } }
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(); } }
我困惑的是对于文本文件的压缩是怎么完成的?
对于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 于宿舍