JavaEE學習日誌(七十九): 裝飾者設計模式,動態代理

JavaEE學習日誌持續更新----> 必看!JavaEE學習路線(文章總彙)

動態代理

裝飾者設計模式

裝飾者設計模式(靜態代理)和動態代理的目的:增強原有對象的功能(連接池方法close)

設計模式基於面向對象思想
裝飾思想在IO中大量應用,所有的緩衝流、打印流都叫裝飾流
Reader(原始流) BufferedReader(裝飾流)
如:地暖,就是地面的裝飾者,保留地面原有的承重功能,添加了取暖效果
使用IO流時,new BufferedReader(原始流)

實現裝飾者模式的步驟

  1. 被裝飾者和裝飾者必須擁有相同的父類,或者實現相同的接口
  2. 使用裝飾者的同時,必須提供被裝飾者,new 裝飾者(被裝飾者)
  3. 方法如果不需要增強,直接使用。需要增強,定義或重寫

自定義裝飾流MyBufferedReader

自定義一個裝飾類
模擬方法readLine()

package com.itheima.decoration;

import java.io.IOException;
import java.io.Reader;

/*
    自定義一個裝飾類
    模擬方法readLine()
 */
public class MyBufferedReader extends Reader {
    private Reader r;
    //new MyBufferedReader(FileReader())
    public MyBufferedReader(Reader r){
        this.r = r;
    }
    /*
        實現讀取文本一行的功能
        原有的功能不能破壞,read()可以讀取一個字符
        利用原有功能read(),實現讀取一行
     */
    public String readLine() throws IOException{
        int len = 0;
        StringBuilder sb = new StringBuilder();
        //read()返回-1,文件結束
        while ((len = r.read())!= -1){
            //判斷讀取到的字符是不是\r, int和char做運算,char自動變成int
            if(len == '\r'){
                continue;
            }
            //判斷讀取到的字符是不是\n
            if(len == '\n'){
                //這一行讀取結束
                //從容器中,取出字符串返回
                return sb.toString();
            }
            //有效的字符,追加緩衝區
            sb.append((char)len);
        }
        //文件讀取結束,判斷緩衝區中是否還有內容
        if(sb.length()>0){
            return sb.toString();
        }
        return null;
    }

    @Override
    public int read(char[] chars, int i, int i1) throws IOException {
        return 0;
    }

    @Override
    public void close() throws IOException {
        r.close();
    }
}

測試類

public class Demo {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("d:\\1.txt");
        //創建自定義的裝飾類,傳遞原始流
        MyBufferedReader my = new MyBufferedReader(fr);
        String line = null;
        while((line=my.readLine())!=null){
            System.out.println(line);
        }
        my.close();
    }
}

在這裏插入圖片描述

類加載器

類加載器(ClassLoader)
作用:加載運行的class文件進入內存,並創建class對象。
在這裏插入圖片描述

動態代理原理

在這裏插入圖片描述

Proxy工具類

實現動態代理

  • 被代理對象 :ArrayList (方法:add,size,get,remove)
  • 代理對象,只要使用被代理對象的方法,通過代理對象
    如:代理對象讓別人只能用ArrayList的size方法

Proxy類的方法

  • static Object newProxyInstance​(ClassLoader loader, 類<?>[] interfaces, InvocationHandler h) 返回指定接口的代理實例,該接口將方法調用分派給指定的調用處理程序。

方法參數:

  1. 傳遞被代理對象的加載器ArrayList.class.getClassLoader
  2. 被代理對象實現的所有接口ArrayList.class.getInterfaces
  3. InvocationHandler接口實現類,代理對象執行的方法

返回值Object:被代理後的對象

InvocationHandler接口方法

  • Object invoke​(Object proxy, 方法 method, Object[] args) 處理代理實例上的方法調用並返回結果。

方法參數:

  1. proxy: 被代理的對象ArrayList
  2. method:被代理對象的方法,如:add,size,get,remove
  3. args:被代理對象的方法的實際參數

返回值Object:執行被代理對象的方法的返回值。如:被代理對象的size() 返回長度

實現動態代理:讓被代理對象ArrayList只能調用size()方法

public class Demo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        //方法getProxy,返回被代理後的對象
        List<String> proxyList = getProxy(list);
        System.out.println(proxyList.size());//3
        System.out.println(proxyList.get(0));//java.lang.RuntimeException: 禁止調用該方法
    }
    /*
        定義方法,實現ArrayList對象的動態代理
        生成被代理的對象,執行集合的方法size被允許
        傳遞被代理的對象,返回代理後的對象
     */
    public static List<String> getProxy(ArrayList<String> list){
        //傳遞被代理對象的加載器
        //傳遞被代理對象實現的接口
        //傳遞InvocationHandler接口的實現類
        //返回值:被代理後的對象
        return (List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
            /*
                被代理對象執行的方法
                調用集合的任意方法,都會執行invoke
                proxy:被代理的對象
                method:被代理對象調用的方法
                objects:調用方法傳遞的實際參數
                返回值:被調用方法的返回值

                Method類方法:getName()獲取執行的方法名
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //不是size方法,就不讓調用
                String name = method.getName();
                if("size".equals(name)){
                    //是size,允許調用方法,size()返回長度
                    return method.invoke(list,args);
                }else {
                    throw new RuntimeException("禁止調用該方法");
                }
            }
        });
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章