程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JDK 6動態編譯—內存字符串編譯方式

JDK 6動態編譯—內存字符串編譯方式

編輯:關於JAVA

JDK6開始提供了動態編譯的API,在許多應用場景都可以用得著,如動態加載(修改)服務、高性動態業務邏輯實現(用腳本或模板引擎實現效率滿足不了需求)等都非常好用。
API對應的接口都在javax.tools包下面,常用編譯方式有基於文本文件、內存字符串等,實際上基於URI的字節流都可以,也就是遠程Java源代碼也可以。對於常用的已有文件形式的動態編譯網上的實例已經非常多,我在這裡介紹下動態編譯內存中以字符串的形式。
簡單的代碼流程如下:

Java代碼
  1. //通過系統工具提供者獲得動態編譯器
  2. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  3. //獲得一個文件管理器,它的功能主要是提供所有文件操作的規則,
  4. //如源代碼路徑、編譯的classpath,class文件目標目錄等,其相關屬性都提供默認值
  5. StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
  6. //獲得CompilationTask並調用
  7. //獲得CompilationTask方法原型:
  8. getTask(Writer out,
  9. JavaFileManager fileManager,
  10. DiagnosticListenersuper JavaFileObject> diagnosticListener,
  11. Iterable options,
  12. Iterable classes,
  13. Iterableextends JavaFileObject> compilationUnits)
  14. //簡單調用例子
  15. boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();
 

我這裡介紹的字符串形式的編譯(其它方式也會有相似的具體實現),還需要提供一個FileObject一個實現類,將相應的對象封裝作為getTask()的最後一個參數來構建具體的編譯Task.
JavaDoc提供的一個FileObject參考實現:
Class JavaSourceFromString
Java代碼
  1. import Java.Net.URI;
  2. import javax.tools.SimpleJavaFileObject;
  3. public class JavaSourceFromString extends SimpleJavaFileObject {
  4. /**
  5. * 源碼
  6. */
  7. final String code;
  8. /**
  9. * 構造方法:從字符串中構造一個FileObject
  10. * @param name the name of the compilation unit represented by this file object
  11. * @param code the source code for the compilation unit represented by this file object
  12. */
  13. JavaSourceFromString(String name, String code) {
  14. super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),
  15. Kind.SOURCE);
  16. this.code = code;
  17. }
  18. @Override
  19. public CharSequence getCharContent(boolean ignoreEncodingErrors) {
  20. return code;
  21. }
  22. }
 


完整的測試類:
Class TestDyCompile

Java代碼
  1. import Java.io.File;
  2. import Java.io.IOException;
  3. import Java.util.Arrays;
  4. import javax.tools.JavaCompiler;
  5. import javax.tools.JavaFileManager.Location;
  6. import javax.tools.JavaFileObject;
  7. import javax.tools.StandardJavaFileManager;
  8. import Javax.tools.StandardLocation;
  9. import Javax.tools.ToolProvider;
  10. import dyclass.Test;
  11. public class TestDyCompile {
  12. /**
  13. *
  14. * @author ZhangXiang
  15. * @param args
  16. * 2011-4-7
  17. */
  18. public static void main(String[] args) {
  19. StringBuilder classStr = new StringBuilder("package dyclass;public class Foo implements Test{");
  20. classStr.append("public void test(){");
  21. classStr.append("System.out.println(\"Foo2\");}}");
  22. JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
  23. StandardJavaFileManager fileManager = jc.getStandardFileManager(null, null, null);
  24. Location location = StandardLocation.CLASS_OUTPUT;
  25. File[] outputs = new File[]{new File("bin/")};
  26. try {
  27. fileManager.setLocation(location, Arrays.asList(outputs));
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. JavaFileObject jfo = new JavaSourceFromString("dyclass.Foo", classStr.toString());
  32. JavaFileObject[] jfos = new JavaFileObject[]{jfo};
  33. Iterableextends JavaFileObject> compilationUnits = Arrays.asList(jfos);
  34. boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();
  35. if(b){//如果編譯成功
  36. try {
  37. Test t = (Test) Class.forName("dyclass.Foo").newInstance();
  38. t.test();
  39. } catch (InstantiationException e) {
  40. e.printStackTrace();
  41. } catch (IllegalAccessException e) {
  42. e.printStackTrace();
  43. } catch (ClassNotFoundException e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. }
  48. }
 


我在這裡的具體業務類為dyclass.Foo,也就是我們需要動態編譯的類,為了方便寫業務的調用代碼,也可以讓我們的業務類實現一個接口,然後通過反射獲得具體子類強制轉換來調用。
Test接口:

Java代碼
  1. public interface Test {
  2. //業務方法簽名
  3. void test();
  4. }


另外,在代碼中還有這麼一段:

Java代碼
  1. Location location = StandardLocation.CLASS_OUTPUT;
  2. File[] outputs = new File[]{new File("bin/")};
  3. try {
  4. fileManager.setLocation(location, Arrays.asList(outputs));
  5. } catch (IOException e) {
  6. e.printStackTrace();
  7. }

這段代碼的作用相信大家一看到它就想到它的作用了,前面有說過JavaFileManager 的作用,我在這裡設置了CLASS文件的輸出目錄,意圖很簡單,我的工程是在Eclipse運行的,項目的目標路徑就是項目下的bin目錄,如果不設置的話,class文件輸出路徑即為默認值,也就是直接在項目根路徑下,後面直接調用就不能完成了。當然在其它一些應用場景中需要設置為自己需要的目錄。
同樣的方法可以設置JavaFileManager 其它的我們需要的文件規則屬性(可以參照枚舉類型StandardLocation),在這裡就不一一介紹了。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved