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方法的声明如下:
- int run(InputStream in,
- OutputStream out,
- OutputStream err,
- String... arguments)使用给定 I/O 通道和参数运行工具。按照惯例,工具如果运行成功,则返回 0;如果出现错误,则返回非 0 值。任何生成的诊断都将以某种未指定的格式写入 out 或 err。
- 参数:
- in - “标准”输入;如果为 null,则使用 System.in
- out - “标准”输出;如果为 null,则使用 System.out
- err - “标准”错误;如果为 null,则使用 System.err
- arguments - 要传递给工具的参数
- 返回:
- 如果成功,则返回 0;否则返回非 0 值
- 抛出:
- NullPointerException - 如果参数数组包含任何 null 元素。
实例源码,注释比较详细,不再解释,Compiler.java中代码片段:
- /**
- * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
- * @param sFullFileName: the java source file name with full path
- * @return bRet: true-compile successfully, false - compile unsuccessfully
- * Description: Compile java source file to java class with run method
- */
- public boolean compileFile(String sFullFileName)
- {
- boolean bRet = false ;
- // get compiler
- JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();
- // compile the java source code by run method
- int iCompileRet = oJavaCompiler.run( null , null , null , sFullFileName);
- // set compile result
- if ( 0 == iCompileRet)
- {
- bRet = true ;
- }
- return bRet;
- }
再看下我们的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方法的声明:
- JavaCompiler.CompilationTask getTask(Writer out,
- JavaFileManager fileManager,
- DiagnosticListener<? super JavaFileObject> diagnosticListener,
- Iterable<String> options,
- Iterable<String> classes,
- Iterable<? extends JavaFileObject> compilationUnits) 使用给定组件和参数创建编译任务的 future。该编译可能没有完成,正如 CompilationTask 接口中所述。
- 如果提供了文件管理器,则它必须能够处理 StandardLocation 中定义的所有位置。
- 参数:
- out - 用于来自编译器的其他输出的 Writer;如果为 null,则使用 System.err
- fileManager - 文件管理器;如果为 null,则使用编译器的标准文件管理器
- diagnosticListener - 诊断侦听器;如果为 null,则使用编译器的默认方法报告诊断信息
- options - 编译器选项;null 表示没有选项
- classes - 类名称(用于注释处理),null 表示没有类名称
- compilationUnits - 要编译的编译单元;null 表示没有编译单元
- 返回:
- 表示编译的对象
- 抛出:
- RuntimeException - 如果在用户提供的组件中发生不可恢复的错误。cause 为用户代码中的错误。
- IllegalArgumentException - 如果给定的任一编译单元具有不同于 source 的类型
源码清单如下,Compiler.java中代码片段:
- /**
- * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
- * @param sFullFileName: the java source file name with full path
- * @param sOutputPath: the output path of java class file
- * @return bRet: true-compile successfully, false - compile unsuccessfully
- * Description: Compile java source file to java class with getTask
- * method, it can specify the class output path and catch diagnostic
- * information
- * @throws IOException
- */
- public boolean compileFile(String sFullFileName, String sOutputPath) throws IOException
- {
- boolean bRet = false ;
- // get compiler
- JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();
- // define the diagnostic object, which will be used to save the
- // diagnostic information
- DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new DiagnosticCollector<JavaFileObject>();
- // get StandardJavaFileManager object, and set the diagnostic for the
- // object
- StandardJavaFileManager oStandardJavaFileManager = oJavaCompiler
- .getStandardFileManager(oDiagnosticCollector, null , null );
- // set class output location
- Location oLocation = StandardLocation.CLASS_OUTPUT;
- try
- {
- oStandardJavaFileManager.setLocation(oLocation, Arrays
- .asList(new File[] { new File(sOutputPath) }));
- // get JavaFileObject object, it will specify the java source file.
- Iterable<? extends JavaFileObject> oItJavaFileObject = oStandardJavaFileManager
- .getJavaFileObjectsFromFiles(Arrays.asList(new File(
- sFullFileName)));
- // compile the java source code by using CompilationTask's call
- // method
- bRet = oJavaCompiler.getTask(null , oStandardJavaFileManager,
- oDiagnosticCollector, null , null , oItJavaFileObject).call();
- //print the Diagnostic's information
- for (Diagnostic oDiagnostic : oDiagnosticCollector
- .getDiagnostics())
- {
- System.out.println("Error on line: "
- + oDiagnostic.getLineNumber() + "; URI: "
- + oDiagnostic.getSource().toString());
- }
- }
- catch (IOException e)
- {
- //exception process
- System.out.println("IO Exception: " + e);
- throw e;
- }
- finally
- {
- //close file manager
- if ( null != oStandardJavaFileManager)
- {
- oStandardJavaFileManager.close();
- }
- }
- return bRet;
- }
编译的方法就这两个简单吧, 下面我们测试下这两个方法:
首先, 声明下我们的compiler类的对象,初始化下编译的类和输出类的路径,MyMain.java中代码片段:
- // get compiler object
- Compiler oCompiler = new Compiler();
- // the java source file name with full path
- String sFullFileName = "E:\\myspace\\CompilerSample\\Sample.java";
- // define the output path of java class, since this demo is ran into
- // eclipse, so set it as bin
- String sOutputPath = "bin/";
测试run方法:
- // Compile java source file to java class with run method
- boolean bRet = oCompiler.compileFile(sFullFileName);
- // print result
- if (bRet)
- {
- System.out.println("Compile the source code \"" + sFullFileName
- + "\" successfully" );
- }
- else
- {
- System.out.println("Compile the source code \"" + sFullFileName
- + "\" unsuccessfully" );
- }
run方法测试,控制台信息:
- Compile the source code "E:\myspace\CompilerSample\Sample.java" successfully
生成的类文件抓图:
测试getTask方法,并利用java反射运行所生成类中的"printClassName"方法:
- // Compile java source file, and output the class file into specified
- // path
- bRet = oCompiler.compileFile(sFullFileName, sOutputPath);
- // print result
- if (bRet)
- {
- System.out.println("Compile the source code \"" + sFullFileName
- + "\" successfully" );
- // if compile success, then execute the printClassName method of the
- // compiled class
- System.out
- .println("Execute the printClassName method of the compiled class: " );
- System.out.print(" " );
- // load the class
- Class oClass = Class.forName("Sample" );
- // new an object of sample class
- Object oObject = oClass.newInstance();
- // get object of printClassName method
- Method oMethod = oClass.getMethod("printClassName" );
- oMethod.invoke(oObject);
- }
- else
- {
- System.out.println("Compile the source code \"" + sFullFileName
- + "\" unsuccessfully" );
- }
- }
运行测试方法后,控制台打印信息:
- Compile the source code "E:\myspace\CompilerSample\Sample.java" successfully
- Execute the printClassName method of the compiled class:
- Print the class name: Sample
生成的类文件抓图:
至此, 通过java Compiler API动态编译并运行源文件的例子就完了.
Java_Java Compiler 应用实例的更多相关文章
- CoreCLR源码探索(八) JIT的工作原理(详解篇)
在上一篇我们对CoreCLR中的JIT有了一个基础的了解, 这一篇我们将更详细分析JIT的实现. JIT的实现代码主要在https://github.com/dotnet/coreclr/tree/m ...
- Vue源码分析之实现一个简易版的Vue
目标 参考 https://cn.vuejs.org/v2/guide/reactivity.html 使用 Typescript 编写简易版的 vue 实现数据的响应式和基本的视图渲染,以及双向绑定 ...
- 最近学习工作流 推荐一个activiti 的教程文档
全文地址:http://www.mossle.com/docs/activiti/ Activiti 5.15 用户手册 Table of Contents 1. 简介 协议 下载 源码 必要的软件 ...
- hbase协处理器编码实例
Observer协处理器通常在一个特定的事件(诸如Get或Put)之前或之后发生,相当于RDBMS中的触发器.Endpoint协处理器则类似于RDBMS中的存储过程,因为它可以让你在RegionSer ...
- Thrift-java实例
➠更多技术干货请戳:听云博客 Thrift实例1 功能描述:客户端与服务器端分别是两个应用,先启动服务器端,再启动客户端,实现执行客户端运行服务器端的加法方法. 源码截图(源码在附件中): 客户端: ...
- Compiler Theory(编译原理)、词法/语法/AST/中间代码优化在Webshell检测上的应用
catalog . 引论 . 构建一个编译器的相关科学 . 程序设计语言基础 . 一个简单的语法制导翻译器 . 简单表达式的翻译器(源代码示例) . 词法分析 . 生成中间代码 . 词法分析器的实现 ...
- JAVA上百实例源码以及开源项目
简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级.中级.高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情.执着,对IT的憧憬. ...
- Java_java动态编译整个项目,解决jar包找不到问题
java动态编译整个项目,解决jar包找不到问题原文:http://itzyx.com/index.php/javac/ 动态将java文件编译为class文件解决方案:将temp\sdl\src目录 ...
- Java_Java SE6调用动态编译
转自:http://www.cnblogs.com/flyoung2008/archive/2011/11/14/2249017.html 一.使用JavaCompiler接口编译java源程序 我们 ...
随机推荐
- org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance:
详细错误堆栈信息: org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" ...
- Linux重置root密码步骤
1.开机时任意按一个方向键,进入界面,选择linux系统,按e键进入2.然后用上下键选择kerner(内核)那一行,按e键进入编辑界面,编辑界面最后一行显示如下:(grub edit> kern ...
- hpunix下11gRac的安装
一.检查环境 1.操作系统版本# uname -a 2.补丁包三大补丁包#swlist -l bundle|grep QPKAPPS#swlist -l bundle|grep QPKBASE#swl ...
- windows phone SDK 8.0 模拟器异常 0x89721800解决办法
删除 APPDATA\LOCAL\Microsoft\Phone Tools\CoreCon\10.0 从新启动即可!
- UI中 frame 与 transform的用法与总结
在iOS中,我们是不可以直接访问控件中frame的结构体的成员的,因此我们需要分三步来改变一个UI控件的位置,大小 一.frame用法 frame的结构体类型为: struct CGRect { CG ...
- LoadRunner参数化之数据生成方式
一般需要使用多条数据来完成实际事务的时候,需要参数化.而使用参数化可以方便实现很多实际事务,记得在哪里看到过,参数化是比C函数更高级的函数. 参数化的方法 先来个最常见的LR示例的登录脚本: Acti ...
- Servlet域对象ServletContext小应用------计算网站访问量
package cn.yzu; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.Servlet ...
- AOP动态代理解析3-增强方法的获取
对于指定bean的增强方法的获取一定是包含两个步骤的: 获取所有的增强 寻找所有增强中使用于bean的增强并应用 那么findCandidateAdvisors与findAdvisorsThatCan ...
- css3 -- 伪类与伪元素
伪类: 1.结构伪类 A:E : first-child{} E : nth-*(n){} E : first-*(even){} E : first-*(odd){} B:nth-child 是根 ...
- Delphi 包的设计思想及它与PAS、BPL、DCU、DLL、OXC的关系。
DCP ,BPL分别是什么文件,起什么作用?你在DELPHI中建立一个package然后保存一下,看看. bpl和Dll比较相似.只是BPL是BORLAND自己弄出来的东西!!!调用也和调用DLL相似 ...