利用JavaCompiler动态编译代码

Java Compiler API

在Java 6中javax.tools提供了标准的包来操作Java编译器,使得我们可以在程序运行期中动态加载Java类,来实现个性化的操作。实际应用可以在一些业务变化多的服务中,将共同操作抽象出来后,再针对不同业务场景动态加载编写的有业务属性的逻辑。

以一个简单的实际例子:

新建抽象基础类:JavaDynamicScript

1
2
3
4
5
public abstract class JavaDynamicScript {

public abstract Object script(Map<String, Object> context);

}

编写动态类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.jinzl.compiler.dto.JavaDynamicScript;
import java.util.Map;
public class HelloWorldScript extends JavaDynamicScript {

private String content;

@Override
public Object script(Map<String, Object> context) {
System.out.println("脚本开始执行");
System.out.println("内部变量content: " + content);
System.out.println("执行上下文context: " + context);
System.out.println("脚本结束执行");
return content;
}
}

包装JavaCompilier

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class JavaCodeCompiler {

public JavaCodeCompiler() throws MalformedURLException {}

private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

private final String libDir = StringUtils.substringBeforeLast(ResourceUtils.extractArchiveURL(JavaDynamicScript.class.getResource("")).getPath(), "/");
private final String classPath = System.getProperty("java.class.path");
private static final String JAVA_CODE_FILE_PATH = "string";

private static final StringJavaFileManager STRING_JAVA_FILE_MANAGER = CompilerConfig.standardFileManager();


public boolean compiler(String fullClassName, String sourceCode, DiagnosticCollector<JavaFileObject> diagnosticCollector){
long startTime = System.currentTimeMillis();
long compilerTakeTime;

JavaFileObject javaFileObject = new StringJavaFileObject(JAVA_CODE_FILE_PATH, fullClassName, sourceCode);
Iterable<String> options = Arrays.asList("-classpath", classPath, "-extdirs", libDir);

JavaCompiler.CompilationTask task = compiler.getTask(null, STRING_JAVA_FILE_MANAGER, diagnosticCollector, options, null, Arrays.asList(javaFileObject));
compilerTakeTime = System.currentTimeMillis() - startTime;

System.out.println("script compile success, class=" + fullClassName + ", time=" + compilerTakeTime);
return task.call();
}

}

使用 ToolProvider.getSystemJavaCompiler() 拿到JavaCompiler接口的实例。
这里还需要提供文件管理服务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringJavaFileObject extends SimpleJavaFileObject {

private String contents;

public StringJavaFileObject(String path, String className, String contents){
super(URI.create(path + ":///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
this.contents = contents;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return contents;
}
}

评论