`

Java Compiler 应用实例

    博客分类:
  • java
阅读更多

一直在用JDK1.5, 一直搞不清楚JDK1.6有啥特性, 就翻了翻, 发现这个Compiler API(JSR 199)动态编译Java源文件功能很有意思. Compiler API如果和反射功能一起使用, 就可以实现java源代码的动态编译并执行这些代码,有点动态语言的特征. 利用这些API普通用户也可以方便的开发自己的编译器,动态生成代码,编译并运行. 本文就通过一个动态编译并运行源文件的例子简单说明下Compile API的基本功能, 有兴趣的可以深入研究下. 本实例的完成工程代码可以从这里 下载: http://dl.iteye.com/topics/download/0807c557-4f0d-3aba-956f-9fe5c9b83962

实例中实现的功能描述:

1. 使用JavaCompiler对象的run方法编译java源代码,并在源代码所在目录生成对应的class文件

2. 使用JavaCompiler对象的getTask方法编译java源代码,并将对应的class文件生成到指定目录, 并执行所生成类中指定的"printClassName"方法

环境准备:

首先回顾一下JDK, JRE,JVM的概念和关系:

JRE是java的运行环境, 说白了有JRE才能运行java类; 同时java类是运行于虚拟机(JVM)上的, 其实虚拟机是JRE的一部分, 具体来讲,在windows上就是JRE下面的一个JVM.dll文件; JDK就是java开发工具箱, 具有编译java类的功能和运行java类的功能(自身包含了一个JRE).

知道了JDK,JRE,JVM的关系,我们就应该明白,如果要在eclipse里面使用java的编译功能必须在eclipse里面使用JDK作为Library,否则在eclipse中获取不了JavaCompiler的对象. 设置如下图:

懒得找JDK1.6,我就直接下载了个1.7装了下,然后开发工具使用MyEclipse (当然用的是免费版的 -:)).

在看我们的实例分析及源码:

首先看下run方法编译java源文件, run方法比较简单,但不能指定输出路径,监控错误信息, 调用后就在源码所在目录生成class文件,run方法的声明如下:

[plain] view plain copy
  1. int run(InputStream in,  
  2.         OutputStream out,  
  3.         OutputStream err,  
  4.         String... arguments)使用给定 I/O 通道和参数运行工具。按照惯例,工具如果运行成功,则返回 0;如果出现错误,则返回非 0 值。任何生成的诊断都将以某种未指定的格式写入 out 或 err。   
  5.   
  6. 参数:  
  7. in - “标准”输入;如果为 null,则使用 System.in  
  8. out - “标准”输出;如果为 null,则使用 System.out  
  9. err - “标准”错误;如果为 null,则使用 System.err  
  10. arguments - 要传递给工具的参数   
  11. 返回:  
  12. 如果成功,则返回 0;否则返回非 0 值   
  13. 抛出:   
  14. NullPointerException - 如果参数数组包含任何 null 元素。  

实例源码,注释比较详细,不再解释,Compiler.java中代码片段:
[java] view plain copy
  1. /**  
  2.      * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  3.      * @param sFullFileName: the java source file name with full path  
  4.      * @return bRet: true-compile successfully, false - compile unsuccessfully  
  5.      * Description: Compile java source file to java class with run method  
  6.      */   
  7.     public   boolean  compileFile(String sFullFileName)  
  8.     {  
  9.         boolean  bRet =  false ;  
  10.         // get compiler   
  11.         JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();  
  12.         // compile the java source code by run method   
  13.         int  iCompileRet = oJavaCompiler.run( null null null , sFullFileName);  
  14.         // set compile result   
  15.         if  ( 0  == iCompileRet)  
  16.         {  
  17.             bRet = true ;  
  18.         }  
  19.         return  bRet;  
  20.     }  

再看下我们的getTask方法编译java源代码, 这个方法其实是构造了一个JavaCompiler.CompilationTask对象, 然后在调用这个对象的call方法, 在构造对象的过程中, 可以指定class的生成路径,监控错误信息,调用过程如下:

1) 生成JavaCompiler对象,用于构造CompilationTask对象,并编译java源代码

2) 构造DiagnosticCollector对象,用于存储诊断信息

3) 构造oStandardJavaFileManager对象,用于设置类的生成路径, 为了方便使用java反射方法,我直接将本实例中的输出路径设置为工程bin目录.实际应用中应根据场景生成道不同的目录--比如可以根据配置或者包名来做.

4) 生成源文件迭代器Iterable对象, 用于存储java源代码文件完整的路径

5) 根据上面生成的对象, 调用JavaCompiler对象的getTask构造CompilationTask对象, 并调用其call方法,编译源代码

再看下getTask方法的声明:

[plain] view plain copy
  1. JavaCompiler.CompilationTask getTask(Writer out,  
  2.                                      JavaFileManager fileManager,  
  3.                                      DiagnosticListener<? super JavaFileObject> diagnosticListener,  
  4.                                      Iterable<String> options,  
  5.                                      Iterable<String> classes,  
  6.                                      Iterable<? extends JavaFileObject> compilationUnits) 使用给定组件和参数创建编译任务的 future。该编译可能没有完成,正如 CompilationTask 接口中所述。   
  7. 如果提供了文件管理器,则它必须能够处理 StandardLocation 中定义的所有位置。   
  8.   
  9.   
  10. 参数:  
  11. out - 用于来自编译器的其他输出的 Writer;如果为 null,则使用 System.err  
  12. fileManager - 文件管理器;如果为 null,则使用编译器的标准文件管理器  
  13. diagnosticListener - 诊断侦听器;如果为 null,则使用编译器的默认方法报告诊断信息  
  14. options - 编译器选项;null 表示没有选项  
  15. classes - 类名称(用于注释处理),null 表示没有类名称  
  16. compilationUnits - 要编译的编译单元;null 表示没有编译单元   
  17. 返回:  
  18. 表示编译的对象   
  19. 抛出:   
  20. RuntimeException - 如果在用户提供的组件中发生不可恢复的错误。cause 为用户代码中的错误。   
  21. IllegalArgumentException - 如果给定的任一编译单元具有不同于 source 的类型  

源码清单如下,Compiler.java中代码片段:
[java] view plain copy
  1. /**  
  2.      * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  3.      * @param sFullFileName: the java source file name with full path  
  4.      * @param sOutputPath: the output path of java class file  
  5.      * @return bRet: true-compile successfully, false - compile unsuccessfully  
  6.      * Description: Compile java source file to java class with getTask  
  7.      *     method, it can specify the class output path and catch diagnostic  
  8.      *     information  
  9.      * @throws IOException   
  10.      */   
  11.     public   boolean  compileFile(String sFullFileName, String sOutputPath)  throws  IOException  
  12.     {  
  13.         boolean  bRet =  false ;  
  14.         // get compiler   
  15.         JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();  
  16.   
  17.         // define the diagnostic object, which will be used to save the   
  18.         // diagnostic information   
  19.         DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new  DiagnosticCollector<JavaFileObject>();  
  20.   
  21.         // get StandardJavaFileManager object, and set the diagnostic for the   
  22.         // object   
  23.         StandardJavaFileManager oStandardJavaFileManager = oJavaCompiler  
  24.                 .getStandardFileManager(oDiagnosticCollector, null null );  
  25.   
  26.         // set class output location   
  27.         Location oLocation = StandardLocation.CLASS_OUTPUT;  
  28.         try   
  29.         {  
  30.             oStandardJavaFileManager.setLocation(oLocation, Arrays  
  31.                     .asList(new  File[] {  new  File(sOutputPath) }));  
  32.   
  33.             // get JavaFileObject object, it will specify the java source file.   
  34.             Iterable<? extends  JavaFileObject> oItJavaFileObject = oStandardJavaFileManager  
  35.                     .getJavaFileObjectsFromFiles(Arrays.asList(new  File(  
  36.                             sFullFileName)));  
  37.   
  38.             // compile the java source code by using CompilationTask's call   
  39.             // method   
  40.             bRet = oJavaCompiler.getTask(null , oStandardJavaFileManager,  
  41.                     oDiagnosticCollector, null null , oItJavaFileObject).call();  
  42.   
  43.             //print the Diagnostic's information   
  44.             for  (Diagnostic oDiagnostic : oDiagnosticCollector  
  45.                     .getDiagnostics())  
  46.             {  
  47.                 System.out.println("Error on line: "   
  48.                         + oDiagnostic.getLineNumber() + "; URI: "   
  49.                         + oDiagnostic.getSource().toString());  
  50.             }  
  51.         }  
  52.         catch  (IOException e)  
  53.         {  
  54.             //exception process   
  55.             System.out.println("IO Exception: "  + e);  
  56.             throw  e;  
  57.         }  
  58.         finally   
  59.         {  
  60.             //close file manager   
  61.             if  ( null  != oStandardJavaFileManager)  
  62.             {  
  63.                 oStandardJavaFileManager.close();  
  64.             }  
  65.         }  
  66.         return  bRet;  
  67.     }  

编译的方法就这两个简单吧, 下面我们测试下这两个方法:

首先, 声明下我们的compiler类的对象,初始化下编译的类和输出类的路径,MyMain.java中代码片段:

[plain] view plain copy
  1. // get compiler object  
  2. Compiler oCompiler = new Compiler();  
  3. // the java source file name with full path  
  4. String sFullFileName = "E:\\myspace\\CompilerSample\\Sample.java";  
  5. // define the output path of java class, since this demo is ran into  
  6. // eclipse, so set it as bin  
  7. String sOutputPath = "bin/";  

测试run方法:
[java] view plain copy
  1. // Compile java source file to java class with run method   
  2.  boolean  bRet = oCompiler.compileFile(sFullFileName);  
  3.  // print result   
  4.  if  (bRet)  
  5.  {  
  6.      System.out.println("Compile the source code \""  + sFullFileName  
  7.              + "\" successfully" );  
  8.  }  
  9.  else   
  10.  {  
  11.      System.out.println("Compile the source code \""  + sFullFileName  
  12.              + "\" unsuccessfully" );  
  13.  }  
run方法测试,控制台信息:
[plain] view plain copy
  1. Compile the source code "E:\myspace\CompilerSample\Sample.java" successfully  

生成的类文件抓图:

测试getTask方法,并利用java反射运行所生成类中的"printClassName"方法:

[java] view plain copy
  1. // Compile java source file, and output the class file into specified   
  2.         // path   
  3.         bRet = oCompiler.compileFile(sFullFileName, sOutputPath);  
  4.         // print result   
  5.         if  (bRet)  
  6.         {  
  7.             System.out.println("Compile the source code \""  + sFullFileName  
  8.                     + "\" successfully" );  
  9.             // if compile success, then execute the printClassName method of the   
  10.             // compiled class   
  11.             System.out  
  12.                     .println("Execute the printClassName method of the compiled class: " );  
  13.             System.out.print("  " );  
  14.             // load the class   
  15.             Class oClass = Class.forName("Sample" );  
  16.             // new an object of sample class   
  17.             Object oObject = oClass.newInstance();  
  18.             // get object of printClassName method   
  19.             Method oMethod = oClass.getMethod("printClassName" );  
  20.             oMethod.invoke(oObject);  
  21.         }  
  22.         else   
  23.         {  
  24.             System.out.println("Compile the source code \""  + sFullFileName  
  25.                     + "\" unsuccessfully" );  
  26.         }  
  27.     }  

运行测试方法后,控制台打印信息:
[plain] view plain copy
  1. Compile the source code "E:\myspace\CompilerSample\Sample.java" successfully  
  2. Execute the printClassName method of the compiled class:   
  3.   Print the class name: Sample  

生成的类文件抓图:

至此, 通过java Compiler API动态编译并运行源文件的例子就完了.

注: 转载请注明出处: http://hejiangtao.iteye.com 用于商业得给我分成大笑

2
1
分享到:
评论
3 楼 jyjava 2012-02-09  
这篇文章不错,可以在面试的时候跟面试官扯扯,实际应用应该很少
2 楼 jyjava 2012-02-09  
直接调用javac,生成的class文件位置无法自由控制
1 楼 mfkvfn 2012-02-09  
没发现比直接调用JDK中的javac命令要方便多少呀?

相关推荐

    JavaCompiler --JDK6 API的简介(java动态编译)

    在实际应用中,JavaCompiler 广泛应用于 Java 动态编译中,例如,在 Web 开发中,我们可以使用 JavaCompiler 来动态编译 Java 源代码,以便实现更加灵活的开发。 JavaCompiler 是 Java 中的一个重要的编译器接口,...

    通过JavaCompiler进行编译java文件(转载)

    要使用JavaCompiler,首先需要获取`ToolProvider`的实例,这是访问所有工具(包括JavaCompiler)的入口点: ```java JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); ``` 然后,我们需要创建一个`...

    java动态编译指定java文件且将java文件生成jar

    本教程将深入讲解如何实现这个过程,主要关注`java动态编译`和`将class生成jar`这两个关键步骤,同时会涉及到`JavaCompiler` API的使用。 首先,让我们了解`JavaCompiler` API。这是Java标准库中的一部分,位于`...

    Java应用程序和java Web调用Matlab配置实例

    然后,使用MATLAB Compiler SDK将M文件编译为Java类。这一步会生成一个Java包,其中包含一个或多个Java类,这些类封装了M文件的功能。在编译过程中,你可能需要设置一些编译选项,比如输入输出参数类型,以适应Java...

    Modern Compiler Implementation in Java, 2nd Edition

    总而言之,《Modern Compiler Implementation in Java, 2nd Edition》是一本详细讲解现代编译器设计和实现的权威教材,它不仅覆盖了编译器各个阶段的理论知识,而且还提供了丰富的实践项目和实例,非常适合计算机...

    Modern+Compiler+Implementation+in+Java

    这些模式提供了解决常见问题的标准方案,如MVC(模型-视图-控制器)模式用于Web应用的结构组织,工厂模式和依赖注入在对象创建和管理上的应用,以及单例模式在保证类只有一个实例时的使用。 Android开发与Java紧密...

    JAVA源码文本-dynamic-java-compiler:DynamicJavaCompiler-根据需要从文本源编译Java代码段

    编写动态执行的Java应用程序需要一些样板代码:使用类加载器,编译错误处理等。该库的目的是使您摆脱这种开发,并使您专注于业务逻辑。 它是如何工作的? 使用该库,动态编译任务非常简单。 假设我们要编译用户动态...

    Java6动态编译案例

    在提供的压缩包文件中,"内存中编译"和"Java6内存中动态编译案例"可能包含了一些实际操作的示例代码,这些代码可能演示了如何在内存中创建源代码字符串,然后使用`JavaCompiler`编译这些源代码,而无需将它们写入...

    A Trace-based Java JIT Compiler Retrofitted from a Method-based Compiler

    这些案例涵盖了不同类型的应用程序,证明了trace-JIT的广泛适用性。 3. **问题讨论**:作者还讨论了基于追踪的编译过程中可能遇到的问题,特别是从编译器优化的角度出发。这些问题包括如何平衡编译时间和运行时开销...

    java 动态编译.java文件,动态运行类 _1.2

    使用`ToolProvider`类获取`JavaCompiler`实例,这是启动编译过程的第一步。 ```java JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); ``` 3. **设置标准文件描述符** `...

    java解析ASN.1文件的实例和简要使用说明

    JAC(Java ASN.1 Compiler)是用于处理ASN.1文件的工具,它提供了编译器和运行时库,使得开发者能够在Java环境中方便地进行ASN.1数据的编码和解码。在这个示例中,我们有一个eclipse工程,这意味着你可以直接在...

    java 动态编译.java文件,动态运行类

    4. **执行编译任务**:使用`JavaCompiler`实例的`compile()`方法执行编译任务。 ```java CompilationTask task = compiler.getTask(null, fileManager, null, options, null, compileFiles); boolean success = task...

    Java 动态编译源码并调用

    首先,我们需要获取`ToolProvider`的实例,然后使用`getSystemJavaCompiler()`方法来获取`JavaCompiler`实例。接着,我们创建`StandardJavaFileManager`,用来管理源文件和类文件。最后,通过`JavaCompiler`的`...

    内存中动态编译执行java代码

    在Java中,我们可以使用Java的`javax.tools.JavaCompiler`接口及其相关的API来实现这个功能。 首先,`javax.tools.JavaCompiler`是Java编译器工具接口,它是Java 6引入的一个新特性,提供了在运行时动态编译Java...

    java动态编译java源文件

    它们包含了编译Java源文件所需的所有工具,如`JavaCompiler`类,用于创建`ToolProvider`实例,该实例可以调用编译器服务。 ```java import javax.tools.*; import java.util.*; public class DynamicCompiler { ...

    MATLAB应用案例:MATLAB和Java的结合使用案例.zip

    本资料"MATLAB应用案例:MATLAB和Java的结合使用案例"可能包含多个示例,展示了如何在MATLAB环境中调用Java代码以及在Java程序中使用MATLAB的功能。以下是关于MATLAB和Java结合使用的一些关键知识点: 1. **MATLAB...

    动态编译字符串成java,并且添加class到jvm

    使用`JavaCompiler.compile()`方法,传入源代码的`Iterable&lt;JavaFileObject&gt;`,以及之前创建的文件管理者和诊断监听器。 5. **创建源代码对象**: 对于动态编译的字符串,我们可以使用`JavaFileObject`的子类`...

    Flex3 compiler API (En)

    Flex3 Compiler API 是一款强大的工具,它允许开发者通过Java应用程序来编译Flex应用。这种能力为那些希望利用Java的强大功能与Flex的丰富界面相结合的开发者提供了极大的灵活性。Adobe在2008年发布的《Adobe Flex 3...

Global site tag (gtag.js) - Google Analytics