转自: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方法的声明如下:

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

实例源码,注释比较详细,不再解释,Compiler.java中代码片段:

  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方法的声明:

  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. out - 用于来自编译器的其他输出的 Writer;如果为 null,则使用 System.err
  10. fileManager - 文件管理器;如果为 null,则使用编译器的标准文件管理器
  11. diagnosticListener - 诊断侦听器;如果为 null,则使用编译器的默认方法报告诊断信息
  12. options - 编译器选项;null 表示没有选项
  13. classes - 类名称(用于注释处理),null 表示没有类名称
  14. compilationUnits - 要编译的编译单元;null 表示没有编译单元
  15. 返回:
  16. 表示编译的对象
  17. 抛出:
  18. RuntimeException - 如果在用户提供的组件中发生不可恢复的错误。cause 为用户代码中的错误。
  19. IllegalArgumentException - 如果给定的任一编译单元具有不同于 source 的类型

源码清单如下,Compiler.java中代码片段:

  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. // define the diagnostic object, which will be used to save the
  17. // diagnostic information
  18. DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new  DiagnosticCollector<JavaFileObject>();
  19. // get StandardJavaFileManager object, and set the diagnostic for the
  20. // object
  21. StandardJavaFileManager oStandardJavaFileManager = oJavaCompiler
  22. .getStandardFileManager(oDiagnosticCollector, null ,  null );
  23. // set class output location
  24. Location oLocation = StandardLocation.CLASS_OUTPUT;
  25. try
  26. {
  27. oStandardJavaFileManager.setLocation(oLocation, Arrays
  28. .asList(new  File[] {  new  File(sOutputPath) }));
  29. // get JavaFileObject object, it will specify the java source file.
  30. Iterable<? extends  JavaFileObject> oItJavaFileObject = oStandardJavaFileManager
  31. .getJavaFileObjectsFromFiles(Arrays.asList(new  File(
  32. sFullFileName)));
  33. // compile the java source code by using CompilationTask's call
  34. // method
  35. bRet = oJavaCompiler.getTask(null , oStandardJavaFileManager,
  36. oDiagnosticCollector, null ,  null , oItJavaFileObject).call();
  37. //print the Diagnostic's information
  38. for  (Diagnostic oDiagnostic : oDiagnosticCollector
  39. .getDiagnostics())
  40. {
  41. System.out.println("Error on line: "
  42. + oDiagnostic.getLineNumber() + "; URI: "
  43. + oDiagnostic.getSource().toString());
  44. }
  45. }
  46. catch  (IOException e)
  47. {
  48. //exception process
  49. System.out.println("IO Exception: "  + e);
  50. throw  e;
  51. }
  52. finally
  53. {
  54. //close file manager
  55. if  ( null  != oStandardJavaFileManager)
  56. {
  57. oStandardJavaFileManager.close();
  58. }
  59. }
  60. return  bRet;
  61. }

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

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

  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方法:

  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方法测试,控制台信息:

  1. Compile the source code "E:\myspace\CompilerSample\Sample.java" successfully

生成的类文件抓图:

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

  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. }

运行测试方法后,控制台打印信息:

  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动态编译并运行源文件的例子就完了.

Java_Java Compiler 应用实例的更多相关文章

  1. CoreCLR源码探索(八) JIT的工作原理(详解篇)

    在上一篇我们对CoreCLR中的JIT有了一个基础的了解, 这一篇我们将更详细分析JIT的实现. JIT的实现代码主要在https://github.com/dotnet/coreclr/tree/m ...

  2. Vue源码分析之实现一个简易版的Vue

    目标 参考 https://cn.vuejs.org/v2/guide/reactivity.html 使用 Typescript 编写简易版的 vue 实现数据的响应式和基本的视图渲染,以及双向绑定 ...

  3. 最近学习工作流 推荐一个activiti 的教程文档

    全文地址:http://www.mossle.com/docs/activiti/ Activiti 5.15 用户手册 Table of Contents 1. 简介 协议 下载 源码 必要的软件 ...

  4. hbase协处理器编码实例

    Observer协处理器通常在一个特定的事件(诸如Get或Put)之前或之后发生,相当于RDBMS中的触发器.Endpoint协处理器则类似于RDBMS中的存储过程,因为它可以让你在RegionSer ...

  5. Thrift-java实例

    ➠更多技术干货请戳:听云博客 Thrift实例1 功能描述:客户端与服务器端分别是两个应用,先启动服务器端,再启动客户端,实现执行客户端运行服务器端的加法方法. 源码截图(源码在附件中): 客户端: ...

  6. Compiler Theory(编译原理)、词法/语法/AST/中间代码优化在Webshell检测上的应用

    catalog . 引论 . 构建一个编译器的相关科学 . 程序设计语言基础 . 一个简单的语法制导翻译器 . 简单表达式的翻译器(源代码示例) . 词法分析 . 生成中间代码 . 词法分析器的实现 ...

  7. JAVA上百实例源码以及开源项目

    简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...

  8. Java_java动态编译整个项目,解决jar包找不到问题

    java动态编译整个项目,解决jar包找不到问题原文:http://itzyx.com/index.php/javac/ 动态将java文件编译为class文件解决方案:将temp\sdl\src目录 ...

  9. Java_Java SE6调用动态编译

    转自:http://www.cnblogs.com/flyoung2008/archive/2011/11/14/2249017.html 一.使用JavaCompiler接口编译java源程序 我们 ...

随机推荐

  1. java中常用的工具类(二)

    下面继续分享java中常用的一些工具类,希望给大家带来帮助! 1.FtpUtil           Java   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  2. [LeetCode] Ugly Number

    Ugly Number Total Accepted: 20760 Total Submissions: 63208 Difficulty: Easy Write a program to check ...

  3. 攻城狮在路上(叁)Linux(十二)--- Linux的目录与路径

    一.相对路径与绝对路径: A.绝对路径:由根目录/开始写起的路径,例如 /usr/share/doc B.相对路径:不是由根目录/开始写起的路径. 二.目录的相关操作: 1.cd: 目录切换 cd ~ ...

  4. C++ list的基本操作和使用

    转自:http://blog.sina.com.cn/s/blog_6a4aa98201012fhn.html Lists将元素按顺序储存在链表中. 与 向量(vectors)相比, 它允许快速的插入 ...

  5. ok6410,mmu,内存管理

    MMU 一.MMU学习 MMU其实就是一个页表.将虚拟地址通过查表的方式,对应到物理地址去他由一个或一组芯片组成,一般存在与协处理器中. 1.将虚拟地址转化为物理地址 2.访问权限管理 1.1得出mm ...

  6. Effective C++ 之 Item 1: 视C++为一个语言联邦

    Effective C++ Chapter 1. 让自己习惯C++(Accustoming Yourself to C++) Item 1. 视C++为一个语言联邦(View C++ as a fed ...

  7. WORD2007多级列表

    转自玄鸟翩翩 http://hi.baidu.com/shine_yen http://hi.baidu.com/shine_yen/item/01ff2255043bc1aeacc85722 用Wo ...

  8. Silverlight中的TabControl如何绑定数据?重写tabcontrol和tabItem 解决绑定友好问题。可以绑定对象集合

    在 WPF 中,TabControl 可以直接将 ItemsSource 绑定数据源,见 将 TabControl 绑定到数据的示例 http://msdn.microsoft.com/zh-cn/l ...

  9. ubuntu12.04 安装eclipse

    1:去官网下载最新版的eclipse for linux; 2:cd  /usr/local 用命令 sudo mkdir eclipse 建立一个Eclipse的目录 3:将下载的文件copy到ec ...

  10. Oracle资源

    ORACLE 10g下载地址 oracle 下载还需要用户名我自己注册了个方便大家使用下载 user:1603869780@qq.compass:qwe123QWE现在直接点击不能下载了 要经过ora ...