上一篇博客是最基本的動態代理原理的實現,因為其固定了接口,固定了代理方法,以及固定了代理的類型,
接下來的博客系類將一步步漸入深度介紹java的動態代理的實現原理
****************************************************************************
首先補充一下“java代理”的相關知識,靜態代理的實現包括兩種方式,一是聚合,另一種是繼承。
聚合是指通過接口,調用其實現類的具體方法:比如接口i,含有方法run(); 類A 實現了接口i,當然
也實現了方法run(); 類B于是就可以通過new一個接口 i 的對象,調用A的run()方法,并在run()方法前后實現其他操作,
這樣就實現了對A的run()方法的代理;
繼承當然更好理解,就是把run()方法重寫,從而實現代理
但是,一旦代理的操作很多,需要寫的類都非常的繁雜,就需要不斷的寫代理類,不斷的更新代理操作,于是這就有了動
態代理。
*****************************************************************************
這次要對實現任意接口的類進行代理。
1、接口
public interface Moveable { void move();}
2、被代理的對象
1 public class Tank implements Moveable { 2 3 @Override 4 public void move() { 5 6 System.out.); 7 try { 8 Thread.sleep(new Random().nextInt(10000)); 9 } catch (InterruptedException e) {10 e.printStackTrace();11 }12 13 }14 15 }
3、用于產生代理對象
1 public class Proxy { 2 //產生新的動態代理類 3 public static Object newProxyInstance(Class intf) throws Exception{ //將接口當成參數傳入,這樣就可以代理實現了任意接口的類,而不僅是實現了Moveable接口 4 //將一下字符串動態編譯,生成代理類 5 //實現方式有以下擊中:jdk6.0 complier API;CGLib; ASM 6 String rt = "/r/n"; 7 String src = 8 "public class TankTimeProxy implements " +intf.getName()+ "{"+rt+ //直接用intf,是調用toString方法,前會加入字符串 interface 9 intf.getName()+" t;"+rt+10 11 " public TankTimeProxy("+intf.getName()+" t) {"+rt+12 " this.t = t;"+rt+13 " }"+rt+14 15 " @Override"+rt+16 " public void move() {"+rt+17 " long start = System.currentTimeMillis();"+rt+18 " System.out.println(/"start time is /"+start);"+rt+19 " t.move();"+rt+20 " long end = System.currentTimeMillis();"+rt+21 " System.out.println(/"end time is /"+end);"+rt+22 " System.out.println(/"time is /"+(end - start));"+rt+23 " }"+rt+24 "}";25 26 //進行編譯27 String fileName = "g:/src/TankTimeProxy.java";//將文件另外存儲,不放置在工程的默認路徑,防止緩沖區相關類的沖突28 File f = new File(fileName);29 FileWriter fw = new FileWriter(f);30 //System.out.println(fileName);31 fw.write(src); //寫入內容32 33 fw.flush();34 fw.close();35 36 //進行編譯37 //首先獲得編譯器38 //compiler 為java編譯器 javac39 //獲得編譯器對象40 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();41 //System.out.println(compiler.getClass().getName());//取得類名42 //參數含義 (編譯診斷,locale,charset)43 //管理動態生成的文件44 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,null,null);//默認值45 //根據參數獲取多個java文件 返回java文件對象46 Iterable units = fileManager.getJavaFileObjects(fileName);47 48 //“編譯任務”對象49 JavaCompiler.CompilationTask task = compiler.getTask(null,fileManager,null,null,null,units);50 task.call();//調用51 fileManager.close();52 53 //************以上過程獲得了java文件源碼,編譯生成了java文件的class文件*******54 //加載至內存,生成新對象55 //Class.load(0 是加載path路徑的class文件56 //URLClassLoader是將硬盤中的class文件加載進入57 58 //通過Url引入本地文件59 URL[] urls = new URL[]{new URL("file:/"+"g:/src/")}; //指定生成class文件的位置,與java文件放置在同一目錄60 //去指定路徑尋找class文件61 URLClassLoader urlClassLoader = new URLClassLoader(urls);62 63 Class c = urlClassLoader.loadClass("TankTimeProxy");64 65 System.out.println(c);66 67 //執行68 //c.newInstance(); 是調用空的構造方法69 70 //獲得構造方法71 //根據java虛擬機,每一個構造方法也相當于一個對象72 Constructor constructor = c.getConstructor(intf);73 74 //產生新對象75 Moveable m = (Moveable) constructor.newInstance(new Tank()); //new Tank()為構造方法的參數 即被代理對象76 77 m.move();78 79 return m;80 }81 }
4、測試端
1 public class Client {2 public static void main(String[] args) throws Exception {3 4 //這里任然以Moveable接口為例傳入,因為在Proxy中為節省麻煩,還是固定生成了Moveable,后來會慢慢簡化的,其實是可以傳遞任意的的接口的5 Moveable m =(Moveable) Proxy.newProxyInstance(Moveable.class);6 m.move();7 8 }9 }
5、結果
(1)生成的java與class文件
(2)java文件的代碼
(3)運行結果
新聞熱點
疑難解答