JDK 6动态编译—内存字符串编译方式
2013-04-21 15:29
162 查看
JDK6开始提供了动态编译的API,在许多应用场景都可以用得着,如动态加载(修改)服务、高性动态业务逻辑实现(用脚本或模板引擎实现效率满足不了需求)等都非常好用。
API对应的接口都在javax.tools包下面,常用编译方式有基于文本文件、内存字符串等,实际上基于URI的字节流都可以,也就是远程Java源代码也可以。对于常用的已有文件形式的动态编译网上的实例已经非常多,我在这里介绍下动态编译内存中以字符串的形式。
简单的代码流程如下:
Java代码
我这里介绍的字符串形式的编译(其它方式也会有相似的具体实现),还需要提供一个FileObject一个实现类,将相应的对象封装作为getTask()的最后一个参数来构建具体的编译Task.
JavaDoc提供的一个FileObject参考实现:
Class JavaSourceFromString
Java代码
完整的测试类:
Class TestDyCompile
Java代码
我在这里的具体业务类为dyclass.Foo,也就是我们需要动态编译的类,为了方便写业务的调用代码,也可以让我们的业务类实现一个接口,然后通过反射获得具体子类强制转换来调用。
Test接口:
Java代码
另外,在代码中还有这么一段:
Java代码
这段代码的作用相信大家一看到它就想到它的作用了,前面有说过JavaFileManager 的作用,我在这里设置了CLASS文件的输出目录,意图很简单,我的工程是在Eclipse运行的,项目的目标路径就是项目下的bin目录,如果不设置的话,class文件输出路径即为默认值,也就是直接在项目根路径下,后面直接调用就不能完成了。当然在其它一些应用场景中需要设置为自己需要的目录。
同样的方法可以设置JavaFileManager 其它的我们需要的文件规则属性(可以参照枚举类型StandardLocation),在这里就不一一介绍了。
原址:http://developer.51cto.com/art/201104/254942.htm
API对应的接口都在javax.tools包下面,常用编译方式有基于文本文件、内存字符串等,实际上基于URI的字节流都可以,也就是远程Java源代码也可以。对于常用的已有文件形式的动态编译网上的实例已经非常多,我在这里介绍下动态编译内存中以字符串的形式。
简单的代码流程如下:
Java代码
//通过系统工具提供者获得动态编译器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); //获得一个文件管理器,它的功能主要是提供所有文件操作的规则, //如源代码路径、编译的classpath,class文件目标目录等,其相关属性都提供默认值 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); //获得CompilationTask并调用 //获得CompilationTask方法原型: getTask(Writer out, JavaFileManager fileManager, DiagnosticListenersuper JavaFileObject> diagnosticListener, Iterable options, Iterable classes, Iterableextends JavaFileObject> compilationUnits) //简单调用例子 boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();
我这里介绍的字符串形式的编译(其它方式也会有相似的具体实现),还需要提供一个FileObject一个实现类,将相应的对象封装作为getTask()的最后一个参数来构建具体的编译Task.
JavaDoc提供的一个FileObject参考实现:
Class JavaSourceFromString
Java代码
import java.net.URI; import javax.tools.SimpleJavaFileObject; public class JavaSourceFromString extends SimpleJavaFileObject { /** * 源码 */ final String code; /** * 构造方法:从字符串中构造一个FileObject * @param name the name of the compilation unit represented by this file object * @param code the source code for the compilation unit represented by this file object */ JavaSourceFromString(String name, String code) { super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); this.code = code; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; } }
完整的测试类:
Class TestDyCompile
Java代码
import java.io.File; import java.io.IOException; import java.util.Arrays; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import dyclass.Test; public class TestDyCompile { /** * * @author ZhangXiang * @param args * 2011-4-7 */ public static void main(String[] args) { StringBuilder classStr = new StringBuilder("package dyclass;public class Foo implements Test{"); classStr.append("public void test(){"); classStr.append("System.out.println(\"Foo2\");}}"); JavaCompiler jc = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = jc.getStandardFileManager(null, null, null); Location location = StandardLocation.CLASS_OUTPUT; File[] outputs = new File[]{new File("bin/")}; try { fileManager.setLocation(location, Arrays.asList(outputs)); } catch (IOException e) { e.printStackTrace(); } JavaFileObject jfo = new JavaSourceFromString("dyclass.Foo", classStr.toString()); JavaFileObject[] jfos = new JavaFileObject[]{jfo}; Iterableextends JavaFileObject> compilationUnits = Arrays.asList(jfos); boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call(); if(b){//如果编译成功 try { Test t = (Test) Class.forName("dyclass.Foo").newInstance(); t.test(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }
我在这里的具体业务类为dyclass.Foo,也就是我们需要动态编译的类,为了方便写业务的调用代码,也可以让我们的业务类实现一个接口,然后通过反射获得具体子类强制转换来调用。
Test接口:
Java代码
public interface Test { //业务方法签名 void test(); }
另外,在代码中还有这么一段:
Java代码
Location location = StandardLocation.CLASS_OUTPUT; File[] outputs = new File[]{new File("bin/")}; try { fileManager.setLocation(location, Arrays.asList(outputs)); } catch (IOException e) { e.printStackTrace(); }
这段代码的作用相信大家一看到它就想到它的作用了,前面有说过JavaFileManager 的作用,我在这里设置了CLASS文件的输出目录,意图很简单,我的工程是在Eclipse运行的,项目的目标路径就是项目下的bin目录,如果不设置的话,class文件输出路径即为默认值,也就是直接在项目根路径下,后面直接调用就不能完成了。当然在其它一些应用场景中需要设置为自己需要的目录。
同样的方法可以设置JavaFileManager 其它的我们需要的文件规则属性(可以参照枚举类型StandardLocation),在这里就不一一介绍了。
原址:http://developer.51cto.com/art/201104/254942.htm
相关文章推荐
- 三十二、数组、指针和字符串:动态内存分配和释放
- Spring中AOP实现的两种方式之JDK和cglib的动态代理
- 利用C#动态编译功能实现像Javascript中的Eval的功能来将一段字符串进行数学运算
- Java探索之——动态代理(JDK和CGlib方式)
- JDK和CGLib两种方式实现动态代理模式
- 编写一个函数,此函数使用动态存储分配来产生一个字符串的副本。例如函数为strclone,则调用p=strclone(str),将会为一个新的字符串分配和str占内存大小相同的一个字符串,并将字符串st
- 使用JDK和Cglib两种方式动态代理
- 从String中动态(内存中)编译和加载java类
- 动态代理及其两种实现方式(JDK、CGLIB)
- sds(简单动态字符串) 内存预分配优化策略
- 解决android使用gson解析json字符串,并使用混淆编译方式打包apk遇到的问题
- Spring AOP --JDK动态代理方式
- 12月1日 笔记 字符串 动态内存开辟
- 动态代理的两种实现方式(JDK/Cglib)
- java 内存编译-动态编译
- Windows下动态内存分配方式
- Spring的两种代理方式:JDK动态代理和CGLIB动态代理
- 动态申请内存方式和数组的比较
- Windows下动态内存分配方式http://whx.tzgt.gov.cn/newOperate/html/7/71/711/3938.html
- Windows下动态内存分配方式