1. 程式人生 > >設計模式之代理 proxy

設計模式之代理 proxy

AOP面向切面程式設計是動態代理的應用。

首先 什麼是聚合:聚合就是在一個類A中有使用另一個B類作為成員變數,那在A類中就可以呼叫B類中的所有成員變數以及成員函式(而繼承只能呼叫父類的成員函式)。
靜態代理:
靜態代理就是我們明確的知道想要實現的是什麼代理,我們就可以將該代理繼承自某一個介面。
那麼我們可以使得LogProxy和TimeProxy等被代理的類都繼承自一個介面,實現代理的生成。
//當兩個代理都實現的是Movable介面時,我們就可以實現一個個代理之間的互相巢狀,並且可以直接在我們的呼叫類中實現改變。靈活性較高。

 public class TankLogProxy implements Movable {
    Movable t;

    public TankLogProxy(Movable t) {
        this.t = t;
    }

    @Override
    public void move() {
        System.out.println("Tank is started....");
        t.move();
        System.out.println("Tank is stopped....");
    }
}

在這裡插入圖片描述
在這裡插入圖片描述
將時間和日誌代理順序交換。
在這裡插入圖片描述
在這裡插入圖片描述

動態代理就是我們是不知道具體有哪些類需要實現代理。
Spring是直接使用JDK中的動態代理進行生成的

我們新建一個Test類,將需要生成的資訊指定資料夾的位置,執行下面程式,即可在對應的位置生成代理類檔案。

public class Test1 {
    public static void main(String[] args) throws Exception{
        String rt = "\r\n";
        String src =
                "package martina.proxy;" +  rt +
                        "public class **TankTimeProxy** implements **Moveable** {" + rt +
                        "    public TankTimeProxy(Moveable t) {" + rt +
                        "        super();" + rt +
                        "        this.t = t;" + rt +
                        "    }" + rt +

                        "    Moveable t;" + rt +

                        "    @Override" + rt +
                        "    public void move() {" + rt +
                        "        long start = System.currentTimeMillis();" + rt +
                        "        System.out.println(\"starttime:\" + start);" + rt +
                        "        t.move();" + rt +
                        "        long end = System.currentTimeMillis();" + rt +
                        "        System.out.println(\"time:\" + (end-start));" + rt +
                        "    }" + rt +
                        "}";
        String fileName = System.getProperty("user.dir")
                + "/src/martina/proxy/TankTimeProxy.java";
        File f = new File(fileName);
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();
        }
 }

在這裡插入圖片描述
生成java檔案之後,我們還需要將其編譯並載入記憶體。

//compile
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
        Iterable units = fileMgr.getJavaFileObjects(fileName);
        //根據fileMgr和要編譯的內容 拿到編譯任務
        JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
        t.call();
        fileMgr.close();

         // 將硬碟上的java檔案載入入記憶體
        //load into memory and create an instance
        //之所以使用URLClassLoader的原因是我們生成的編譯檔案(class檔案)是在原始碼的資料夾下的,而不是在bin的classPatch下。
        //根據目錄 找到class檔案
        URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir") +"/src")};
        URLClassLoader ul = new URLClassLoader(urls);
        Class c = ul.loadClass("martina.proxy.TankTimeProxy");
        System.out.println(c);

        //拿到Movable的構造器從而例項化出Movable物件,然後呼叫
        Constructor ctr = c.getConstructor(Movable.class);
        Movable m = (Movable)ctr.newInstance(new Tank());
        m.move();

執行main函式就可以得到輸出結果:
在這裡插入圖片描述
那麼其實我們也可以通過傳參的方式將要實現的介面傳入,(上文實現的是介面是固定的Movable介面)
那麼我們就需要將src修改一下,將Movable修改為形參傳過來的介面名,並且將傳來的介面中所有的方法都實現。
當然,實現了介面的傳遞還遠遠不夠,在介面的方法中該怎麼實現處理我們不能直接放在src中,所以我們還需要傳遞進來一個Handler,在實現Handler的各個介面中定義不同的操作,在src中呼叫即可。
所以我們需要定義一個InnovationHandler(),其中定義一個invoke方法即可。
未編譯的類如果 沒有匯入reflect類 ,可能編譯不了?