27、Filter開發(3)(包裝request和response)(JavaEE筆記)

weixin_34249678發表於2016-06-02

主要內容:

  • filter的部署
  • 包裝設計模式對request和response進行包裝增強

一、Filter的部署-註冊Filter

  • <filter-name>:指定過濾器的名字,內容不能為空。

  • <filter-class>: 指定過濾器的完整的限定類名

  • <init-param> 指定初始化引數,其子元素<param-name>指定引數的名字,<param-value>指定引數的值。可以使用FilterConfig介面物件來訪問初始化引數。

  • <filter-mapping> 設定一個Filter所負責攔截的資源。一個Filter攔截的資源可以通過兩種方式來指定:Servlet名稱和資源訪問的請求路徑。

    • <filter-name>子元素用於設定Filter註冊名稱,和上面一樣。
    • <url-pattern>設定Filter所攔截的請求路徑;/表示攔截所有,.jsp表示攔截jsp檔案。等等。
    • <dispatcher>指定過濾器所攔截的資源被Servlet容器呼叫的方式,可以是REQUEST,INCLUDE,FORWARD,ERROR之一(必須是大寫),預設是REQUEST。使用者可以設定多個<dispatcher>子元素來指定Filter對資源的多種呼叫方式進行攔截。
  • <dispatcher>子元素可以設定的值及其意義

    • REQUEST:當使用者直接訪問頁面時,web容器將會呼叫過濾器。如果目標資源是通過RequestDispatcher的include或forward方法訪問時,那麼該過濾器就不會被呼叫。
    • INCLUDE:如果目標資源是通過RequestDispatcher的include方法訪問時,那麼該過濾器將被呼叫,除此之外,該過濾器將不會被呼叫。
    • FORWARD:如果目標資源是通過RequestDispatcher的forward方法訪問時,那麼該過濾器將被呼叫,除此之外,該過濾器將不會被呼叫。
    • ERROR:如果目標資源是通過宣告式異常處理機制呼叫時,那麼該過濾器將被呼叫。除此之外,過濾器不會被呼叫。

示例:
FilterDemo4.java

package cn.itcast.web.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class FilterDemo4 implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("hahah");
    }
    @Override
    public void destroy() {
    }
}

配置:

<filter>
    <filter-name>FilterDemo4</filter-name>
    <filter-class>cn.itcast.web.filter.FilterDemo4</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>FilterDemo4</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>
  <error-page>
    <exception-type>java.lang.ArithmeticException</exception-type>
    <location>/2.jsp</location>
  </error-page>

1.jsp

<body>
    <%
        int x = 1/0;
     %>
  </body>

2.jsp

<body>
error page
</body>

說明:

  • 1.本來我們是在1.jsp中配置errorPage="/2.jsp",但是這樣配置不起作用。於是我們在web.xml中進行配置。試驗時我們訪問1.jsp,那麼就會被過濾器攔截下來,不會到達2.jsp
  • 2.在以後的開發中我們經常要攔截forward轉發的資源,注意在配置檔案中進行配置,不然是不會起作用的。在上個例子中我們可以看到。

二、filter高階開發

由於開發人員在Filter中可以得到代表使用者請求和響應的request、response物件,因此在程式設計中可以使用Decorator(裝飾器)模式對這些物件進行包裝,再把包裝後的物件傳給目標資源,從而實現一些特殊需求。

注意:有四種方式訪問web資源,正常的通過瀏覽器直接訪問(request方式)、forward方式、include方式和error方式。

2.1回顧包裝開發模式

之前我們在筆記20中講到過包裝開發模式,這裡再次回顧一下。使用包裝設計模式對BufferedReader類進行包裝增強。

BufferedReaderWrapper.java

package cn.itcast.demo;

import java.io.BufferedReader;
import java.io.IOException;
//使用包裝設計模式對BufferedReader類進行包裝增強
/*
 * 1.實現與被增強物件相同的介面,如果介面方法太多,也可以繼承一個類
 * 2.定義一個變數記住被增強物件
 * 3.定義一個構造器,接收被增強物件
 * 4.覆蓋需要增強的方法
 * 5.對於不想增強的方法,直接呼叫被增強物件的方法
 * 
 * */
import java.io.Reader;

public class BufferedReaderWrapper extends BufferedReader {
    private BufferedReader br;
    private int linenum = 1;

    public BufferedReaderWrapper(BufferedReader br) {
        super(br);
        // 子類在使用建構函式的時候會呼叫父類的建構函式,但是
        // 這裡不知道呼叫父類哪個建構函式,於是就呼叫無參構造
        // 函式,但是父類又沒有無參建構函式,這樣就會報錯,所
        // 以這裡我們要指定呼叫父類哪個建構函式
        this.br = br;
    }

    @Override
    public String readLine() throws IOException {// 增強此方法
        String line = br.readLine();
        if (line == null) {
            return line;
        }
        return linenum++ + ":" + line;
    }
}

測試:TestBufferedReaderWrapper.java

package cn.itcast.demo;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class TestBufferedReaderWrapper {

    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new FileReader("src/cn/itcast/demo/BufferedReaderWrapper.java"));
        BufferedReaderWrapper wrapper = new BufferedReaderWrapper(br);
        
        /*String line = null;
        while((line = wrapper.readLine()) != null ){
            System.out.println(line);
        }*/
        FileWriter fw = new FileWriter("D:\\1.java");
        String line = null;
        while((line = wrapper.readLine()) != null ){
            fw.write(line + "\r\n");//注意要換行
        }
        fw.close();
        wrapper.close();
        br.close();
    }
}

說明:BufferedReader類中的readLine方法可以讀取一行文字,這裡我們想讓readLine讀取一行文字時還在本行最前面加上行號,於是我們使用包裝設計模式對此方法進行包裝。注意:這裡當此方法讀到空行的時候返回的是"",而不是null。
記住:能用包裝設計模式就不要用子類的方式

2.2Decorator(包裝)設計模式

  • 當某個物件的方法不適應業務需求時,通常有兩種方式可以對方法進行增強:

    • 編寫子類,覆蓋需要增強的方法
    • 使用Decorator設計模式對方法進行增強
    • 使用動態代理(這裡先不講)
  • 在實際開發中遇到需要增強物件的方法時,到底選擇用哪種方式

    • 沒有具體的定式,不過有一種情況下,必須使用Decorator設計模式,即被增強物件,開發人員只能得到它的物件,無法得到它的class檔案。
    • 比如request、response物件,開發人員之所以在Servlet中能通過sun公司定義的HttpServletRequest\HttpServletResponse介面去操作這些物件,是因為tomcat伺服器廠商編寫了request、response介面的實現類。Web伺服器在呼叫Servlet時,會用這些介面的實現類建立出物件,然後傳遞給Servlet程式。
    • 此種情況下,由於開發人員根本不知道伺服器廠商編寫的request、response介面的實現類是哪個,在程式中只能拿到其提供的物件,因此就只能採用Decorator設計模式對這些物件進行增強。
  • Decorator設計模式的實現
    1.首先看需要被增強物件繼承了什麼介面或父類,編寫一個類也去繼承這些介面或父類。
    2.在類中定義一個變數,變數型別即需要增強物件型別。
    3.在類中定義一個建構函式,接收需要增強的物件。
    4.覆蓋需要增強的方法,編寫增強的程式碼。
    使用此設計模式為BufferedReader類的readLine方法新增行號的功能。在上面的例子中我們可以看到。

三、對request物件的增強(工程day18_2

  • ServletAPI中提供了一個request物件的Decorator設計模式的預設實現類HttpServletRequestWrapperHttpServletRequestWrapper類實現了request介面中的所有方法,但這些方法的內部實現都是僅僅呼叫了一下所包裝的request物件的對應方法)以避免使用者在對request物件進行增強時需要實現request介面中的所有方法(這樣太麻煩)。
  • 使用Decorator模式包裝request物件,完全解決get、post請求方式下的亂碼問題(前面有過這樣一個例子,但是那個例子中只能解決post方式下的亂碼問題)。

3.1 包裝request物件,解決亂碼問題

過濾器:CharacterEncodingFilter.java

public class CharacterEncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        // 這裡我們先解決post方式的亂碼問題
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        // 解決get方式的亂碼問題
        MyCharacterEncodingRequest requestWrapper = new MyCharacterEncodingRequest(
                request);

        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void destroy() {
    }
}

class MyCharacterEncodingRequest extends HttpServletRequestWrapper {
    // 這裡我們使用一個變數記住Servlet傳遞的request物件。
    private HttpServletRequest request;

    public MyCharacterEncodingRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    // 增強此方法,此方法得到表單提交的資料
    public String getParameter(String name) {
        String value = this.request.getParameter(name);
        if (value == null) {// 如果為空直接返回空即可
            return null;
        }
        if (!this.request.getMethod().equalsIgnoreCase("get")) {
            // 如果不是get方式則沒必要轉換
            return value;
        }
        try {
            /*
             * value = new
             * String(value.getBytes("ISO8859-1"),"UTF-8");//手工轉換,不要寫死
             */

            value = new String(value.getBytes("ISO-8859-1"),
                    this.request.getCharacterEncoding());// 和Request設定的編碼一致
            return value;// 這裡使用ISO-8859-1沒有解決亂碼,這裡瀏覽器
            // 提交的本就是UTF-8編碼,不需要轉換
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

說明:由於使用request.setCharacterEncoding("UTF-8");方式對get方式提交的資料無效,所以之前那個過濾器只能解決post方式提交的資料亂碼問題。既然這種方式不能解決get方式提交的資料的亂碼問題,那麼我們可以將HttpServletRequest包裝之後再給使用者使用。這裡我們主要是對其方法getParameter進行增強。這樣就解決了get方式提交的亂碼問題。

3.2 包裝request物件,實現html標籤轉義功能

E:\apache\apache-tomcat-8.0.28-src\webapps\examples\WEB-INF\classes\util\HTMLFilter.java提供相應的例子)
HtmlFilter .java

public class HtmlFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request);
        chain.doFilter(myHtmlRequest, response);
        
    }
    @Override
    public void destroy() {
    }
}
class MyHtmlRequest extends HttpServletRequestWrapper{
    
    private HttpServletRequest request;
    public MyHtmlRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }
    @Override
    public String getParameter(String name) {
        String value = this.request.getParameter(name);
        if(value == null){
            return null;
        }
        return filter(value);
    }
    
    public static String filter(String message) {

        if (message == null)
            return (null);

        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuilder result = new StringBuilder(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                result.append("<");
                break;
            case '>':
                result.append(">");
                break;
            case '&':
                result.append("&");
                break;
            case '"':
                result.append(""");
                break;
            default:
                result.append(content[i]);
            }
        }
        return (result.toString());
    }
}

3.3 包裝request物件,實現對髒話進行過濾

DirtyFilter.java

public class DirtyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        
        DirtyRequest dirtyRequest = new DirtyRequest(request);
        chain.doFilter(dirtyRequest, response);
    }
    @Override
    public void destroy() {
    }

}
class DirtyRequest extends HttpServletRequestWrapper{
    private List<String> dirtyWords = Arrays.asList("sb","畜生");
    private HttpServletRequest request ;
    public DirtyRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }
    @Override
    public String getParameter(String name) {
        String value = this.request.getParameter(name);
        if(value == null){
            return null;
        }
        for(String dirtyWord : dirtyWords){
            if(value.contains(dirtyWord)){
                value = value.replace(dirtyWord, "***");
            }
        }
        return value;
    }
}

四、包裝response物件

4.1 包裝response物件,實現壓縮響應

GzipFilter.java

package cn.itcast.web.filter;
//解決全站的壓縮問題,對Response進行增強,但是這個過濾器只是解決壓縮字元,需要把資料都寫到記憶體中去,如果是下載一個大檔案,記憶體可能會崩,這裡我們可以在
//配置檔案中設定只對字元壓縮有效,這攔截jsp,js,css,html
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GzipFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        
        BufferResponse myResponse = new BufferResponse(response);
        chain.doFilter(request, myResponse);
        
        byte out[] = myResponse.getBuffer();//先獲得資料
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        GZIPOutputStream gout = new GZIPOutputStream(bout);//進行壓縮
        gout.write(out);
        gout.close();//一定要關閉,這樣資料才會從快取中寫入到底層流中去
        
        byte gzip[] = bout.toByteArray();//從底層流中取得資料
        response.setHeader("content-encoding", "gzip");//這裡需要告訴瀏覽器這是一個壓縮資料
        response.setContentLength(gzip.length);
        response.getOutputStream().write(gzip);//寫出到瀏覽器
        
    }

    @Override
    public void destroy() {}

}

/*OutputStream out = response.getOutputStream();
out.write("aaaaaa".getBytes());

*之後servlet在使用Response的時候其實是使用我們自己定義的Response,而呼叫的getOutputStream方法也是我們自己定義的,這個方法返回的是
*MyServletOutputStream,然後呼叫write方法也是呼叫我們自己定義的write方法,此方法是將資料寫到ByteArrayOutputStream底層流中。
*
*/


class BufferResponse extends HttpServletResponseWrapper{

    private HttpServletResponse response;
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();//位元組流
    private PrintWriter pw;
    
    public BufferResponse(HttpServletResponse response) {
        super(response);
        this.response = response;
    }
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        //這裡我們對此方法進行了增強,不管是圖片還是文字等資料都會進行壓縮,但是如果直接訪問jsp卻不會,因為jsp一般是呼叫getWriter
        //方法,所以這裡我們需要對getWriter方法進行增強
        return new MyServletOutputStream(bout);
    }
    
    @Override
    public PrintWriter getWriter() throws IOException {
        
        /*return new PrintWriter(bout);//因為PrintWriter有接受一個底層流的建構函式,所以這裡我們不需要重寫一個,但是這個方法也是一個包裝類
        //這個類當快取沒有寫滿的時候是不會講資料寫到底層流中去,所以這裡我們需要強制關閉此類*/   
        //pw = new PrintWriter(bout);//jsp中的漢字是一個字元流,這裡會將其先轉換為位元組流,查的碼錶是gb2312的碼錶,但是我們設定的碼錶是UTF-8
        //而PrintWriter有一個接受一個字元流的方法,而字元流就會有設定碼錶的方法,而OutputStreamWriter是字元流到位元組流的一個轉換流,裡面就可以指定碼錶
        pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));
        return pw;
    }
    
    
    public byte[] getBuffer(){
        try{
            if(pw != null){
                pw.close();
            }
            if(bout != null){
                bout.flush();
                return bout.toByteArray();
            }
            return null;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
class MyServletOutputStream extends ServletOutputStream{
    
    private ByteArrayOutputStream bout ;
    public  MyServletOutputStream(ByteArrayOutputStream bout) {
        this.bout = bout;
    }
    
    @Override
    public void write(int arg0) throws IOException {
        this.bout.write(arg0);
        
    }
    @Override
    public boolean isReady() {
        return false;
    }

    @Override
    public void setWriteListener(WriteListener listener) {}
}

說明:

  • 這個包裝類的實現有點難理解,這裡我們詳細說明一下。這裡是實現資料的壓縮之後再輸出給瀏覽器,而我們完全可以在servlet中先從快取中拿到資料壓縮之後再輸出,但是那樣的話每個需要壓縮資源的servlet都需要編寫重複的程式碼,所以這裡我們使用過濾器進行簡化。
  • 首先伺服器將資源寫給瀏覽器的時候(注意這裡和request過濾器的方向是反的),會被這個瀏覽攔截到。攔截到之後我們對response進行增強。
  • 而一般會呼叫getOutputStream方法和getWriter方法向瀏覽器中寫資料,於是這裡我們對這兩個方法進行增強。
  • 方法getOutputStream會返回一個ServletOutputStream流,我們需要增強,我們要讓此方法返回一個我們自己定義的一個流,於是對此類也進行包裝。
  • 方法getWriter中我們將自己定義的流傳遞給PrintWriter方法。
  • 我們將資料壓縮之後存入到底層流中,之後使用者在呼叫getOutputStream方法和getWriter時拿到的資料就是我們壓縮之後的資料。
  • 其實整個過程就是當使用者呼叫方法向瀏覽器輸出資料的時候我們將response的相關方法進行增強(實現資料壓縮)之後再去呼叫真正response的方法進行輸出,這樣就可以實現壓縮。

配置:

<filter>
    <filter-name>GzipFilter</filter-name>
    <filter-class>cn.itcast.web.filter.GzipFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>
<filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.css</url-pattern>
</filter-mapping>

注意:這裡我們需要配置FORWARD和REQUEST,用於攔截forward請求。因為大多數時候我們都是轉發過來的請求。

4.2 包裝response物件,快取資料到記憶體

CacheFiltet.java

public class CacheFilter implements Filter {
    //實際開發中我們可以使用一些專業的快取工具
    private Map<String, byte[]> map = new HashMap<String, byte[]>();
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        
        //1.得到使用者請求的uri
        String uri = request.getRequestURI();
        
        //2.看換出中有沒有uri對應的資料
        byte b[] =  map.get(uri);
        
        
        //3.如果有,則直接拿快取的資料送給瀏覽器,程式返回
        if(b != null){
            response.getOutputStream().write(b);
            return ;
        }
        
        //4.如果沒有,讓目標資源執行,並捕獲目標資源的輸出
        BufferResponse1 myresResponse1 = new BufferResponse1(response);
        chain.doFilter(request, myresResponse1);
        byte out[] = myresResponse1.getBuffer();
        //5.把資源的資料以使用者請求的uri的關鍵字儲存到快取中
        map.put(uri, out);
        
        //6.把資料送給瀏覽器
        response.getOutputStream().write(out);
    }

    @Override
    public void destroy() {}

}

class BufferResponse1 extends HttpServletResponseWrapper{

    private HttpServletResponse response;
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();//位元組流
    private PrintWriter pw;
    
    public BufferResponse1(HttpServletResponse response) {
        super(response);
        this.response = response;
    }
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        
        return new MyServletOutputStream1(bout);
    }
    
    
    @Override
    public PrintWriter getWriter() throws IOException {
        
        pw = new PrintWriter(new OutputStreamWriter(bout,this.response.getCharacterEncoding()));
        return pw;
    }
    
    public byte[] getBuffer(){
        try{
            if(pw != null){
                //如果pw不為空,則我們需要關閉一下,讓其將資料從快取寫到底層流中去
                pw.close();
            }
            if(bout != null){
                bout.flush();
                return bout.toByteArray();
            }
            return null;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }   
}
class MyServletOutputStream1 extends ServletOutputStream{
    
    private ByteArrayOutputStream bout ;
    public  MyServletOutputStream1(ByteArrayOutputStream bout) {
        this.bout = bout;
    }
    
    @Override
    public void write(int arg0) throws IOException {
        //其實write方法有三種過載形式,但是其內部都是呼叫的這種形式,所以我們只需要過載這種形式即可
        this.bout.write(arg0);
        
    }
    @Override
    public boolean isReady() {
        return false;
    }

    @Override
    public void setWriteListener(WriteListener listener) {}
}

說明:

  • 對於頁面中很少更新的資料,例如商品分類,為避免每次都要從資料庫查詢分類資料,因此可以把分類資料快取在記憶體或檔案中,以此來減輕資料庫壓力,提高系統響應速度。相關書名直接看程式中的註釋即可,這裡不再細說。

五、動態代理

  • 在java裡,每個物件都有一個類與之對應。

  • 現在要生成某一個物件的代理物件,這個代理物件也要通過一個類來生成,所以首先要編寫用於生成代理物件的類。

  • 如何編寫生成代理物件的類,兩個要素:

    • 代理誰
    • 如何生成代理物件
  • 代理誰?
    設計一個類變數,以及一個建構函式,記住代理類代理哪個物件。

  • 如何生成代理物件?
    設計一個方法生成代理物件(在方法內編寫程式碼生成代理物件是此處程式設計的難點)

  • java提供了一個Proxy類,呼叫它的newInstance方法可以生成某個物件的代理物件,使用該方法生成代理物件時,需要三個引數:

    • 1.生成代理物件使用哪個類裝載器
    • 2.生成哪個物件的代理物件,通過介面指定
    • 3.生成的代理物件的方法裡幹什麼事,由開發人員編寫handler介面的實現來指定。
  • 初學者必須理解(記住)

    • Proxy類負責建立代理物件時,如果指定了handler(處理器),那麼不管使用者呼叫代理物件的什麼方法,該方法都是呼叫處理器的invoke方法。
    • 由於invoke方法被呼叫需要三個引數:代理物件、方法、方法的引數,因此不管代理物件哪個方法呼叫處理器的invoke方法,都必須把自己所在的物件、自己(呼叫invoke方法的方法)、方法的引數傳遞進來。

六、動態代理應用

  • 在動態代理技術裡,由於不管使用者呼叫代理物件的什麼方法,都是呼叫開發人員編寫的處理器的invoke方法(這相當於invoke方法攔截到了代理物件的方法呼叫)。

  • 並且,開發人員通過invoke方法的引數,才可以在攔截的同時,知道使用者呼叫的是什麼方法,因此利用這兩個特徵,就可以實現一些特殊需求。例如:攔截使用者的訪問請求,以檢查使用者是否有訪問許可權、動態為某個物件新增額外的功能。

相關文章