熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

Java SE6調用Java編譯器的兩種新方法

2013-11-15 11:44:51  來源: JSP教程 
      在很多Java應用中需要在程序中調用Java編譯器來編譯和運行但在早期的版本中(Java SE及以前版本)中只能通過toolsjar中的comsuntoolsjavac包來調用Java編譯器但由於toolsjar不是標准的Java庫在使用時必須要設置這個jar的路徑而在Java SE中為我們提供了標准的包來操作Java編譯器這就是javaxtools包使用這個包我們可以不用將jar文件路徑添加到classpath中了

  使用JavaCompiler接口來編譯Java源程序

   使用Java API來編譯Java源程序有很多方法現在讓我們來看一種最簡單的方法通過JavaCompiler進行編譯

   我們可以通過ToolProvider類的靜態方法getSystemJavaCompiler來得到一個JavaCompiler接口的實例
JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();
   JavaCompiler中最核心的方法是run通過這個方法可以編譯java源程序這個方法有個固定參數和個可變參數(可變參數是從Jave SE開始提供的一個新的參數類型用type… argu表示)個參數分別用來為java編譯器提供參數得到Java編譯器的輸出信息以及接收編譯器的錯誤信息後面的可變參數可以傳入一個或多個Java源程序文件如果run編譯成功返回

int run(InputStream in OutputStream out OutputStream err String arguments)
   如果前個參數傳入的是null那麼run方法將以標准的輸入輸出代替即SysteminSystemout和Systemerr如果我們要編譯一個testjava文件並將使用標准輸入輸出run的使用方法如下

int results = toolrun(null null null testjava);
   下面是使用JavaCompiler的完整代碼

import javaio*;
import javaxtools*;

public class test_compilerapi
{
  public static void main(String args[]) throws IOException
  {
   JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();
   int results = compilerrun(null null null testjava);
   Systemoutprintln((results == )?編譯成功:編譯失敗);
   // 在程序中運行test
   Runtime run = RuntimegetRuntime();
   Process p = runexec(java test);
   BufferedInputStream in = new BufferedInputStream(pgetInputStream());
   BufferedReader br = new BufferedReader(new InputStreamReader(in));
   String s;
   while ((s = brreadLine()) != null)
    Systemoutprintln(s);
  }
}

public class test
{
  public static void main(String[] args) throws Exception
  {
   Systemoutprintln(JavaCompiler測試成功!);
  }
}
   編譯成功的輸出結果

   編譯成功

   JavaCompiler測試成功

   編譯失敗的輸出結果

testjava:: 找不到符號
符號 方法 printlnln(javalangString)
位置 類 javaioPrintStream
Systemoutprintlnln(JavaCompiler測試成功!);
^
錯誤
編譯失敗

  使用StandardJavaFileManager編譯Java源程序

   在第一部分我們討論調用java編譯器的最容易的方法這種方法可以很好地工作但它確不能更有效地得到我們所需要的信息如標准的輸入輸出信息而在Java SE中最好的方法是使用StandardJavaFileManager類這個類可以很好地控制輸入輸出並且可以通過DiagnosticListener得到診斷信息而DiagnosticCollector類就是listener的實現

   使用StandardJavaFileManager需要兩步首先建立一個DiagnosticCollector實例以及通過JavaCompiler的getStandardFileManager()方法得到一個StandardFileManager對象最後通過CompilationTask中的call方法編譯源程序
在使用這種方法調用Java編譯時最復雜的方法就是getTask下面讓我們討論一下getTask方法這個方法有如下所示的個參數

getTask(Writer outJavaFileManager fileManager
DiagnosticListener<? super JavaFileObject> diagnosticListener
Iterable<String> options
Iterable<String> classes
Iterable<? extends JavaFileObject> compilationUnits)
   這些參數大多數都可為null它們的含義所下

   ·out:用於輸出錯誤的流默認是Systemerr

   ·fileManager:標准的文件管理

   ·diagnosticListener: 編譯器的默認行為

   ·options: 編譯器的選項

   ·classes參與編譯的class

   最後一個參數compilationUnits不能為null因為這個對象保存了你想編譯的Java文件

   在使用完getTask後需要通過StandardJavaFileManager的getJavaFileObjectsFromFiles或getJavaFileObjectsFromStrings方法得到compilationUnits對象調用這兩個方法的方式如下

Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
Iterable<? extends File> files)
Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(
Iterable<String> names)

String[] filenames = …;
Iterable<? extends JavaFileObject> compilationUnits =
fileManagergetJavaFileObjectsFromFiles(ArraysasList(filenames));

JavaCompilerCompilationTask task = compilergetTask(null fileManager
diagnostics options null compilationUnits);
   最後需要關閉fileManagerclose();

   下面是一個完整的演示程序

import javaio*;
import javautil*;
import javaxtools*;

public class test_compilerapi
{
  private static void compilejava() throws Exception
  {
   JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();
   // 建立DiagnosticCollector對象
   DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
   StandardJavaFileManager fileManager = compilergetStandardFileManager(diagnostics null null);
   // 建立用於保存被編譯文件名的對象
   // 每個文件被保存在一個從JavaFileObject繼承的類中
   Iterable<? extends JavaFileObject> compilationUnits =
            fileManagergetJavaFileObjectsFromStrings(ArraysasList(testjava));
   JavaCompilerCompilationTask task = compilergetTask(null fileManager
            diagnostics null null compilationUnits);
   // 編譯源程序
   boolean success = taskcall();
   fileManagerclose();
   Systemoutprintln((success)?編譯成功:編譯失敗);
  }
  public static void main(String args[]) throws Exception
  {
   compilejava();
  }
}
   如果想得到具體的編譯錯誤可以對Diagnostics進行掃描代碼如下

for (Diagnostic diagnostic : diagnosticsgetDiagnostics())
Systemoutprintf(
Code: %s%n +
Kind: %s%n +
Position: %s%n +
Start Position: %s%n +
End Position: %s%n +
Source: %s%n +
Message: %s%n
diagnosticgetCode() diagnosticgetKind()
diagnosticgetPosition() diagnosticgetStartPosition()
diagnosticgetEndPosition() diagnosticgetSource()
diagnosticgetMessage(null));
   被編譯的testjava代碼如下

public class test
{
  public static void main(String[] args) throws Exception
  {
   aa; //錯誤語句
   Systemoutprintln(JavaCompiler測試成功!);
  }
}
   在這段代碼中多寫了個aa得到的編譯錯誤為

Code: compilererrnotstmt
Kind: ERROR
Position:
Start Position:
End Position:
Source: testjava
Message: testjava:: 不是語句
Success: false
   通過JavaCompiler進行編譯都是在當前目錄下生成class文件而使用編譯選項可以改變這個默認目錄編譯選項是一個元素為String類型的Iterable集合如我們可以使用如下代碼在D盤根目錄下生成class文件

Iterable<String> options = ArraysasList(d d:\\);
JavaCompilerCompilationTask task = compilergetTask(null fileManager
diagnostics options null compilationUnits);

  在上面的例子中options處的參數為null而要傳遞編譯器的參數就需要將options傳入

   有時我們編譯一個Java源程序文件而這個源程序文件需要另幾個Java文件而這些Java文件又在另外一個目錄那麼這就需要為編譯器指定這些文件所在的目錄

Iterable<String> options = ArraysasList(sourcepath d:\\src);

   上面的代碼指定的被編譯Java文件所依賴的源文件所在的目錄

  從內存中編譯

   JavaCompiler不僅可以編譯硬盤上的Java文件而且還可以編譯內存中的Java代碼然後使用reflection來運行它們我們可以編寫一個JavaSourceFromString類通過這個類可以輸入Java源代碼一但建立這個對象你可以向其中輸入任意的Java代碼然後編譯和運行而且無需向硬盤上寫class文件


import javalangreflect*;
import javaio*;
import javaxtools*;
import javaxtoolsJavaCompilerCompilationTask;
import javautil*;
import *;

public class test_compilerapi
{
  private static void compilerJava() throws Exception
  {
   JavaCompiler compiler = ToolProvidergetSystemJavaCompiler();
   DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
   // 定義一個StringWriter類用於寫Java程序
   StringWriter writer = new StringWriter();
   PrintWriter out = new PrintWriter(writer);
   // 開始寫Java程序
   outprintln(public class HelloWorld {);
   outprintln( public static void main(String args[]) {);
   outprintln( Systemoutprintln(\Hello World\););
   outprintln( });
   outprintln(});
   outclose();
   //為這段代碼取個名子HelloWorld以便以後使用reflection調用
   JavaFileObject file = new JavaSourceFromString(HelloWorld writertoString());
   Iterable<? extends JavaFileObject> compilationUnits = ArraysasList(file);
   JavaCompilerCompilationTask task = compilergetTask(null null
    diagnostics null null compilationUnits);
   boolean success = taskcall();
   Systemoutprintln(Success: + success);
   // 如果成功通過reflection執行這段Java程序
   if (success)
   {
    Systemoutprintln(輸出);
    ClassforName(HelloWorld)getDeclaredMethod(main new Class[]
            { String[]class })invoke(null new Object[]
            { null });
    Systemoutprintln(輸出 );
   }
  }
  public static void main(String args[]) throws Exception
  {
   compilerJava();
  }
}
// 用於傳遞源程序的JavaSourceFromString類
class JavaSourceFromString extends SimpleJavaFileObject
{
  final String code;
  JavaSourceFromString(String name String code)
  {
   super(URIcreate(string:/// + namereplace( /)+ KindSOURCEextension) KindSOURCE);
   de = code;
  }
  @Override
  public CharSequence getCharContent(boolean ignoreEncodingErrors)
  {
   return code;
  }
}


From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19545.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.