前言:這個是我自己學習《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 於宿舍