JDK 6动态编译—内存字符串编译方式

开发 后端
JDK6开始提供了动态编译的API,在许多应用场景都可以用得着,如动态加载(修改)服务、高性动态业务逻辑实现(用脚本或模板引擎实现效率满足不了需求)等都非常好用。

 

JDK6开始提供了动态编译的API,在许多应用场景都可以用得着,如动态加载(修改)服务、高性动态业务逻辑实现(用脚本或模板引擎实现效率满足不了需求)等都非常好用。

    API对应的接口都在javax.tools包下面,常用编译方式有基于文本文件、内存字符串等,实际上基于URI的字节流都可以,也就是远程Java源代码也可以。对于常用的已有文件形式的动态编译网上的实例已经非常多,我在这里介绍下动态编译内存中以字符串的形式。

简单的代码流程如下:

Java代码
  1. //通过系统工具提供者获得动态编译器     
  2. JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();     
  3. //获得一个文件管理器,它的功能主要是提供所有文件操作的规则,     
  4. //如源代码路径、编译的classpath,class文件目标目录等,其相关属性都提供默认值     
  5. StandardJavaFileManager fileManager = compiler.getStandardFileManager(nullnullnull);     
  6.     
  7. //获得CompilationTask并调用     
  8. //获得CompilationTask方法原型:     
  9. getTask(Writer out,     
  10.      JavaFileManager fileManager,     
  11.      DiagnosticListenersuper JavaFileObject> diagnosticListener,     
  12.      Iterable options,     
  13.      Iterable classes,     
  14.      Iterableextends JavaFileObject> compilationUnits)     
  15.     
  16.     
  17. //简单调用例子     
  18. boolean b = jc.getTask(null, fileManager, nullnullnull, compilationUnits).call();  

    我这里介绍的字符串形式的编译(其它方式也会有相似的具体实现),还需要提供一个FileObject一个实现类,将相应的对象封装作为getTask()的最后一个参数来构建具体的编译Task.

JavaDoc提供的一个FileObject参考实现:

Class JavaSourceFromString

Java代码
  1. import java.net.URI;     
  2.     
  3. import javax.tools.SimpleJavaFileObject;     
  4.     
  5. public class JavaSourceFromString extends SimpleJavaFileObject {     
  6.     
  7.     /**    
  8.      *  源码    
  9.      */    
  10.     final String code;     
  11.     
  12.     /**    
  13.      * 构造方法:从字符串中构造一个FileObject    
  14.      * @param name the name of the compilation unit represented by this file object    
  15.      * @param code the source code for the compilation unit represented by this file object    
  16.      */    
  17.     JavaSourceFromString(String name, String code) {     
  18.         super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),     
  19.               Kind.SOURCE);     
  20.         this.code = code;     
  21.     }     
  22.     
  23.     @Override    
  24.     public CharSequence getCharContent(boolean ignoreEncodingErrors) {     
  25.         return code;     
  26.     }     
  27. }    



完整的测试类:
Class TestDyCompile

Java代码
  1. import java.io.File;     
  2. import java.io.IOException;     
  3. import java.util.Arrays;     
  4.     
  5. import javax.tools.JavaCompiler;     
  6. import javax.tools.JavaFileManager.Location;     
  7. import javax.tools.JavaFileObject;     
  8. import javax.tools.StandardJavaFileManager;     
  9. import javax.tools.StandardLocation;     
  10. import javax.tools.ToolProvider;     
  11.     
  12. import dyclass.Test;     
  13.     
  14.     
  15. public class TestDyCompile {     
  16.     
  17.     /**    
  18.      *     
  19.      * @author ZhangXiang    
  20.      * @param args    
  21.      * 2011-4-7    
  22.      */    
  23.     public static void main(String[] args) {     
  24.              
  25.         StringBuilder classStr = new StringBuilder("package dyclass;public class Foo implements Test{");     
  26.         classStr.append("public void test(){");     
  27.         classStr.append("System.out.println(\"Foo2\");}}");     
  28.              
  29.         JavaCompiler jc = ToolProvider.getSystemJavaCompiler();     
  30.         StandardJavaFileManager fileManager = jc.getStandardFileManager(nullnullnull);     
  31.         Location location = StandardLocation.CLASS_OUTPUT;     
  32.         File[] outputs = new File[]{new File("bin/")};     
  33.         try {     
  34.             fileManager.setLocation(location, Arrays.asList(outputs));     
  35.         } catch (IOException e) {     
  36.             e.printStackTrace();     
  37.         }     
  38.              
  39.         JavaFileObject jfo = new JavaSourceFromString("dyclass.Foo", classStr.toString());     
  40.         JavaFileObject[] jfos = new JavaFileObject[]{jfo};     
  41.         Iterableextends JavaFileObject> compilationUnits = Arrays.asList(jfos);     
  42.         boolean b = jc.getTask(null, fileManager, nullnullnull, compilationUnits).call();     
  43.         if(b){//如果编译成功     
  44.             try {     
  45.                 Test t = (Test) Class.forName("dyclass.Foo").newInstance();     
  46.                 t.test();     
  47.             } catch (InstantiationException e) {     
  48.                 e.printStackTrace();     
  49.             } catch (IllegalAccessException e) {     
  50.                 e.printStackTrace();     
  51.             } catch (ClassNotFoundException e) {     
  52.                 e.printStackTrace();     
  53.             }     
  54.         }     
  55.     }     
  56. }   



我在这里的具体业务类为dyclass.Foo,也就是我们需要动态编译的类,为了方便写业务的调用代码,也可以让我们的业务类实现一个接口,然后通过反射获得具体子类强制转换来调用。

Test接口:

Java代码
  1. public interface Test {     
  2.     //业务方法签名     
  3.     void test();     
  4. }   


另外,在代码中还有这么一段:

Java代码
  1.   Location location = StandardLocation.CLASS_OUTPUT;     
  2.          File[] outputs = new File[]{new File("bin/")};     
  3. try {     
  4.     fileManager.setLocation(location, Arrays.asList(outputs));     
  5. catch (IOException e) {     
  6.     e.printStackTrace();     
  7. }    

这段代码的作用相信大家一看到它就想到它的作用了,前面有说过JavaFileManager 的作用,我在这里设置了CLASS文件的输出目录,意图很简单,我的工程是在Eclipse运行的,项目的目标路径就是项目下的bin目录,如果不设置的话,class文件输出路径即为默认值,也就是直接在项目根路径下,后面直接调用就不能完成了。当然在其它一些应用场景中需要设置为自己需要的目录。
同样的方法可以设置JavaFileManager 其它的我们需要的文件规则属性(可以参照枚举类型StandardLocation),在这里就不一一介绍了。

 

 

责任编辑:金贺 来源: ITEYE博客
相关推荐

2024-04-01 08:41:39

字符串.NET

2022-12-27 09:33:28

2009-07-09 10:05:55

2021-09-07 09:23:07

C++字符串算法

2009-12-17 13:23:25

Ruby eval方法

2024-06-26 08:18:08

ES6模板字符串

2021-09-10 08:18:31

Go语言字符串

2010-02-25 13:58:22

Linux JDK

2009-11-26 13:11:24

PHP字符串

2023-12-11 07:33:05

Go语言字符技巧

2023-10-31 18:57:02

Java字符串

2009-12-01 09:18:50

PHP分割字符串

2009-08-27 16:29:18

C#动态编译

2010-03-23 11:17:16

Python 动态编译

2009-08-04 18:10:35

ASP.NET动态编译

2020-05-13 12:17:33

RedisC字符C语言

2009-08-04 18:05:37

动态编译ASP.NET

2009-02-26 16:28:43

静态编译动态编译Java

2019-03-07 15:43:22

Redis数据SDS

2022-07-15 13:01:13

Kotlin编程语言Java
点赞
收藏

51CTO技术栈公众号