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  於宿舍


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM