天天看点

java把字符串变代码_将字符串转换为代码

如何以编程方式编译以字符串形式提供的Java代码的问题被问相当频繁和以各种形式,有时引用存储在数据库或由用户..当我搜索有关这方面的信息时,我偶然发现了许多这样的问题,并且失望地看到,一般的建议是使用外部工具(BeanShell,Groovy.)。这个亚当·佩恩特对这个问题的回答是最有帮助的,至少找出了相关的关键词。但即使通过咨询更多的外部资源(比如Java 2的示例),我很难实现一个或多个Java类的纯内存编译(实际上是这样的)。工作过)只使用JavaCompilerAPI

下面是一个例子,展示了在运行时在内存中编译一个或多个类的整个过程,当它们的源代码以字符串的形式给出时。它是围绕一个小的实用工具类建造的,RuntimeCompiler,它只接收序列类名和相应的源代码,然后允许编译这些类并获得Class物品。

这是一个MCVE可以直接编译和执行的JDK, 不带着JRE,因为后者不包含像JavaCompiler.import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStream;

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Modifier;

import java.net.URI;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Comparator;

import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import javax.tools.Diagnostic;

import javax.tools.DiagnosticCollector;import javax.tools.FileObject;import javax.tools.ForwardingJavaFileManager;

import javax.tools.JavaCompiler;import javax.tools.JavaCompiler.CompilationTask;import javax.tools.JavaFileObject;

import javax.tools.JavaFileObject.Kind;import javax.tools.SimpleJavaFileObject;import javax.tools.StandardJavaFileManager;

import javax.tools.ToolProvider;public class RuntimeCompilerExample{

public static void main(String[] args) throws Exception

{

simpleExample();

twoClassExample();

useLoadedClassExample();

}

private static void simpleExample()

{

String classNameA = "ExampleClass";

String codeA =

"public class ExampleClass {" + "\n" +

"    public static void exampleMethod(String name) {" + "\n" +

"        System.out.println(\"Hello, \"+name);" + "\n" +

"    }" + "\n" +

"}" + "\n";

RuntimeCompiler r = new RuntimeCompiler();

r.addClass(classNameA, codeA);

r.compile();

MethodInvocationUtils.invokeStaticMethod(

r.getCompiledClass(classNameA),

"exampleMethod", "exampleParameter");

}

private static void twoClassExample()

{

String classNameA = "ExampleClassA";

String codeA =

"public class ExampleClassA {" + "\n" +

"    public static void exampleMethodA(String name) {" + "\n" +

"        System.out.println(\"Hello, \"+name);" + "\n" +

"    }" + "\n" +

"}" + "\n";

String classNameB = "ExampleClassB";

String codeB =

"public class ExampleClassB {" + "\n" +

"    public static void exampleMethodB(String name) {" + "\n" +

"        System.out.println(\"Passing to other class\");" + "\n" +

"        ExampleClassA.exampleMethodA(name);" + "\n" +

"    }" + "\n" +

"}" + "\n";

RuntimeCompiler r = new RuntimeCompiler();

r.addClass(classNameA, codeA);

r.addClass(classNameB, codeB);

r.compile();

MethodInvocationUtils.invokeStaticMethod(

r.getCompiledClass(classNameB),

"exampleMethodB", "exampleParameter");

}

private static void useLoadedClassExample() throws Exception

{

String classNameA = "ExampleComparator";

String codeA =

"import java.util.Comparator;" + "\n" +

"public class ExampleComparator " + "\n" +

"    implements Comparator {" + "\n" +

"    @Override" + "\n" +

"    public int compare(Integer i0, Integer i1) {" + "\n" +

"        System.out.println(i0+\" and \"+i1);" + "\n" +

"        return Integer.compare(i0, i1);" + "\n" +

"    }" + "\n" +

"}" + "\n";

RuntimeCompiler r = new RuntimeCompiler();

r.addClass(classNameA, codeA);

r.compile();

Class> c = r.getCompiledClass("ExampleComparator");

Comparator comparator = (Comparator) c.newInstance();

System.out.println("Sorting...");

List list = new ArrayList(Arrays.asList(3,1,2));

Collections.sort(list, comparator);

System.out.println("Result: "+list);

}}class RuntimeCompiler{

private final JavaCompiler javaCompiler;

private final Map classData;

private final MapClassLoader mapClassLoader;

private final ClassDataFileManager classDataFileManager;

private final List compilationUnits;

public RuntimeCompiler()

{

this.javaCompiler = ToolProvider.getSystemJavaCompiler();

if (javaCompiler == null)

{

throw new NullPointerException(

"No JavaCompiler found. Make sure to run this with "

+ "a JDK, and not only with a JRE");

}

this.classData = new LinkedHashMap();

this.mapClassLoader = new MapClassLoader();

this.classDataFileManager =

new ClassDataFileManager(

javaCompiler.getStandardFileManager(null, null, null));

this.compilationUnits = new ArrayList();

}

public void addClass(String className, String code)

{

String javaFileName = className + ".java";

JavaFileObject javaFileObject =

new MemoryJavaSourceFileObject(javaFileName, code);

compilationUnits.add(javaFileObject);

}

boolean compile()

{

DiagnosticCollector diagnosticsCollector =

new DiagnosticCollector();

CompilationTask task =

javaCompiler.getTask(null, classDataFileManager,

diagnosticsCollector, null, null,

compilationUnits);

boolean success = task.call();

compilationUnits.clear();

for (Diagnostic> diagnostic : diagnosticsCollector.getDiagnostics())

{

System.out.println(

diagnostic.getKind() + " : " +

diagnostic.getMessage(null));

System.out.println(

"Line " + diagnostic.getLineNumber() +

" of " + diagnostic.getSource());

System.out.println();

}

return success;

}

public Class> getCompiledClass(String className)

{

return mapClassLoader.findClass(className);

}

private static final class MemoryJavaSourceFileObject extends

SimpleJavaFileObject

{

private final String code;

private MemoryJavaSourceFileObject(String fileName, String code)

{

super(URI.create("string:///" + fileName), Kind.SOURCE);

this.code = code;

}

@Override

public CharSequence getCharContent(boolean ignoreEncodingErrors)

throws IOException

{

return code;

}

}

private class MapClassLoader extends ClassLoader

{

@Override

public Class> findClass(String name)

{

byte[] b = classData.get(name);

return defineClass(name, b, 0, b.length);

}

}

private class MemoryJavaClassFileObject extends SimpleJavaFileObject

{

private final String className;

private MemoryJavaClassFileObject(String className)

{

super(URI.create("string:///" + className + ".class"),

Kind.CLASS);

this.className = className;

}

@Override

public OutputStream openOutputStream() throws IOException

{

return new ClassDataOutputStream(className);

}

}

private class ClassDataFileManager extends

ForwardingJavaFileManager

{

private ClassDataFileManager(

StandardJavaFileManager standardJavaFileManager)

{

super(standardJavaFileManager);

}

@Override

public JavaFileObject getJavaFileForOutput(final Location location,

final String className, Kind kind, FileObject sibling)

throws IOException

{

return new MemoryJavaClassFileObject(className);

}

}

private class ClassDataOutputStream extends OutputStream

{

private final String className;

private final ByteArrayOutputStream baos;

private ClassDataOutputStream(String className)

{

this.className = className;

this.baos = new ByteArrayOutputStream();

}

@Override

public void write(int b) throws IOException

{

baos.write(b);

}

@Override

public void close() throws IOException

{

classData.put(className, baos.toByteArray());

super.close();

}

}}class MethodInvocationUtils{

public static Object invokeStaticMethod(

Class> c, String methodName, Object... args)

{

Method m = findFirstMatchingStaticMethod(c, methodName, args);

if (m == null)

{

throw new RuntimeException("No matching method found");

}

try

{

return m.invoke(null, args);

}

catch (IllegalAccessException e)

{

throw new RuntimeException(e);

}

catch (IllegalArgumentException e)

{

throw new RuntimeException(e);

}

catch (InvocationTargetException e)

{

throw new RuntimeException(e);

}

catch (SecurityException e)

{

throw new RuntimeException(e);

}

}

private static Method findFirstMatchingStaticMethod(

Class> c, String methodName, Object ... args)

{

Method methods[] = c.getDeclaredMethods();

for (Method m : methods)

{

if (m.getName().equals(methodName) &&

Modifier.isStatic(m.getModifiers()))

{

Class>[] parameterTypes = m.getParameterTypes();

if (areAssignable(parameterTypes, args))

{

return m;

}

}

}

return null;

}

private static boolean areAssignable(Class> types[], Object ...args)

{

if (types.length != args.length)

{

return false;

}

for (int i=0; i

{

Object arg = args[i];

Class> type = types[i];

if (arg != null && !type.isAssignableFrom(arg.getClass()))

{

return false;

}

}

return true;

}}根据评论编辑:

为了编译包含在外部JAR文件中的类,只需将JAR添加到classpath调用应用程序。这个JavaCompiler然后将选择这个类路径来查找编译所需的类。

似乎涉及到了一些魔法。至少,我还没有弄清楚这背后的确切机制,只是用一个例子对其进行了测试。

还有一个旁白:当然,我们可以从字面上考虑任意这个类的扩展。我的目标是创建一个简单、独立、易于复制和传递的示例,它显示了整个过程,甚至可能对某些应用程序模式“有用”。

对于更复杂的功能,可以考虑相应地扩展这个类,或者查看一下,例如,Java-Runtime-编译器来自OpenHFT项目(我在写了这个答案几周后偶然发现了这个问题)。它基本上在内部使用相同的技术,但以一种更复杂的方式使用,并且还提供了用于处理外部依赖项的类加载器的专用机制。