設計模式之代理 proxy
阿新 • • 發佈:2018-12-09
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類 ,可能編譯不了?