天天看點

[Java] 動态代理 02 --生成代理主題角色

現在有出現了一個問題? 如果我現在有多個類,那我是不是要去實作多個計時,多個日志,那不是和剛才的繼承一樣,造成了類的大量産生(重複),這樣顯然是不合理的,那我們帶怎麼辦喃?我們現在就可以使用動态代理

我們來自己寫一個動态代理類,名字叫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接口,而是實作的其他接口),那我們怎麼辦喃?

  那我們把接口也當參數傳進來