• Java_Java Compiler 应用实例


    转自:http://hejiangtao.iteye.com/blog/1399122

    一直在用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:myspaceCompilerSampleSample.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:myspaceCompilerSampleSample.java" successfully  
    2. Execute the printClassName method of the compiled class:   
    3.   Print the class name: Sample  

    生成的类文件抓图:

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

  • 相关阅读:
    Hbase王国游记之:Hbase客户端API初体验
    Hbase给初学者的“下马威”
    五分钟轻松了解Hbase面向列的存储
    JsonBuilder初出茅庐
    如何查看laravel门脸类包含方法的源码
    PHP常用数组函数
    Go语言strings包
    PHP获取远程http或ftp文件的md5值
    Git使用详细教程(9):git log
    PHP Iterator迭代对象属性
  • 原文地址:https://www.cnblogs.com/gisblogs/p/5504306.html
Copyright © 2020-2023  润新知