現在有出現了一個問題? 如果我現在有多個類,那我是不是要去實作多個計時,多個日志,那不是和剛才的繼承一樣,造成了類的大量産生(重複),這樣顯然是不合理的,那我們帶怎麼辦喃?我們現在就可以使用動态代理
我們來自己寫一個動态代理類,名字叫Proxy
源碼:
package com.bjsxt.proxy;
public class Proxy {
//這個類的作用就是用來産生新的代理類
public static Object newProxyInstance() { // JDK6 Complier API, CGLib, ASM
/* 把這個類當成一個string的字元串(源碼)
現在我們假設,我們能把這字元串編譯,生成類,放在記憶體,來産生對象
動态代理就是你看不到代理類,你隻需要調用一個方法( Proxy的newProxyInstance()方法),
會自動給你傳回一個代理類對象,這個對象的産生是由内部動态的生成一段代碼,編譯完成的
*/
String rt = "\r\n";
String src = "package com.bjsxt.proxy;" + rt + rt +
"public class TankTimeProxy implements Moveable {" + rt +
" public TankTimeProxy(Moveable t) {" + rt +
" this.t = t;" + rt +
" }" + rt + rt +
" Moveable t;" + rt + rt +
" @Override" + rt +
" public void move() {" + rt +
" long start=System.currentTimeMillis();" + rt +
" t.move();" + rt +
" long end=System.currentTimeMillis();" + rt +
" System.out.println((end - start));" + rt +
" }" + rt +
"}";
return null ;
}
}
上面的注釋解釋的很清楚了。
現在我們就來動态的編譯這段代碼
一般動态編譯檔案有這些方法(用JDK6的complier API(大于1.6都行,隻是這個是1.6的新特性),CGlib,ASM(直接生成二進制的class檔案))
我們直接用 JDK 的 complier
我們要做的步驟:
(1), 把字元串進行編譯
(2), 生成一個類
(3), 寫入記憶體
(4), 生成對象
下面我們就來一一實作, 我們先寫一個測試類,叫 Test1.java
第一步 : 準備好字元串代碼 String src
第二步 : 利用檔案IO, 生成 TankTimeProxy
Test1.java
package com.bjsxt.compiler.test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class Test1 {
// 這個類的作用就是用來産生新的代理類
public static void main(String[] args) throws IOException { // JDK6 Complier API, CGLib, ASM
/*
* 把這個類當成一個string的字元串(源碼) 現在我們假設,我們能把這字元串編譯,生成類,放在記憶體,來産生對象
*
* 動态代理就是你看不到代理類,你隻需要調用一個方法( Proxy的newProxyInstance()方法),
* 會自動給你傳回一個代理類對象,這個對象的産生是由内部動态的生成一段代碼,編譯完成的
*/
String rt = "\r\n";
String src = "package com.bjsxt.proxy;" + rt + rt +
"public class TankTimeProxy implements Moveable {" + rt +
" public TankTimeProxy(Moveable t) {" + rt +
" this.t = t;" + rt +
" }" + rt + rt +
" Moveable t;" + rt + rt +
" @Override" + rt +
" public void move() {" + rt +
" long start=System.currentTimeMillis();" + rt +
" t.move();" + rt +
" long end=System.currentTimeMillis();" + rt +
" System.out.println((end - start));" + rt +
" }" + rt +
"}";
// 擷取目前系統目錄(就是項目根目錄)
String fileName = System.getProperty("user.dir") + "/src/com/bjsxt/proxy/TankTimeProxy.java";
System.out.println(fileName);
// System.out.println(fileName);
File f = new File(fileName);
FileWriter writer = new FileWriter(f);
writer.write(src);
writer.flush();
writer.close();
// 看是否生成代碼,右鍵項目,重新整理就OK了
}
}
在做這一步之前,如果你的檔案裡有 TankTimeProxy.java 檔案,你把它删除了,不需要了,因為我可以動态的來生成了。
運作代碼,完成之後,右鍵項目,重新整理,你會看到出現了一個 TankTimeProxy.java 檔案 .OK ,第二步完成。
第三步 :我們來生成一個類
// 這句話的作用就是擷取系統目前預設的編譯器(其實就 javac)
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 拿到java的編譯器
System.out.println(compiler.getClass().getName());
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,
null, null);// 檔案的 管理器
Iterable untis = fileMgr.getJavaFileObjects(fileName); // 找到檔案,把檔案放在Iterable(數組)中
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null,untis);// 定好編譯檔案任務
t.call(); // 編譯檔案
fileMgr.close();// 關閉檔案管理器
運作 :
我們把檔案加入記憶體(原本一般的做法是class.loader,就OK了,但是調用這個方法的前提就是,你的 class 檔案目錄必須在 classpath 的檔案目錄下),我們這裡用一種通用的做法
// 這裡使用url加載器
URL[] urls = new URL[] { new URL("file:/"
+ System.getProperty("user.dir") + "/src") };
URLClassLoader ul = new URLClassLoader(urls); // 這裡需要一個數組位址
Class c = ul.loadClass("com.bjsxt.proxy.TankTimeProxy");
// 把類加到記憶體
System.out.println(c);
測試:輸出c,OK,第四步完成
最後一步,生成對象
// 反射來建立對象
Constructor ctr = c.getConstructor(Moveable.class); // 擷取構造方法
Moveable m = (Moveable) ctr.newInstance(new Tank()); // m 是用反射來建立的對象
m.move();
運作結果:
D:\Java\MyEclipse\Workspaces\Proxy/src/com/bjsxt/proxy/TankTimeProxy.java
com.sun.tools.javac.api.JavacTool
class com.bjsxt.proxy.TankTimeProxy
starttime : 1390141936679
Tank Moving...
6156
Ok,我們要求的功能全部實作了。
5.如果現在我們實作不是一個特定的接口(意思就是不是實作Moveable接口,而是實作的其他接口),那我們怎麼辦喃?
那我們把接口也當參數傳進來