我想知道是否有任何方法可以将String
Java 转换为Java可编译代码.
我有一个比较表达式保存在数据库字段中.我想从数据库中检索它,然后在条件结构中进行评估.
有没有办法做到这一点?
如果您使用的是Java 6,则可以尝试使用Java Compiler API.其核心是JavaCompiler类.您应该能够Comparator
在内存中构造对象的源代码.
警告:我实际上没有尝试过下面的代码,因为JavaCompiler对象在我的平台上不可用,原因有些奇怪......
警告:编译任意Java代码可能会危害您的健康.
考虑自己警告......
String comparableClassName = ...; // the class name of the objects you wish to compare String comparatorClassName = ...; // something random to avoid class name conflicts String source = "public class " + comparatorClassName + " implements Comparable<" + comparableClassName + "> {" + " public int compare(" + comparableClassName + " a, " + comparableClassName + " b) {" + " return " + expression + ";" + " }" + "}"; JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); /* * Please refer to the JavaCompiler JavaDoc page for examples of the following objects (most of which can remain null) */ Writer out = null; JavaFileManager fileManager = null; DiagnosticListener super JavaFileObject> diagnosticListener = null; Iterableoptions = null; Iterable classes = null; Iterable extends JavaFileObject> compilationUnits = new ArrayList extends JavaFileObject>(); compilationUnits.add( new SimpleJavaFileObject() { // See the JavaDoc page for more details on loading the source String } ); compiler.getTask(out, fileManager, diagnosticListener, options, classes, compilationUnits).call(); Comparator comparator = (Comparator) Class.forName(comparableClassName).newInstance();
在此之后,您只需将适当的Java表达式存储在数据库字段中,引用a
和b
.
作为一个字符串,给出了如何以编程方式编译Java代码的问题是问相当频繁,并以各种形式,有时指的是存储在一个代码数据库或通过输入用户.当我搜索有关这方面的信息时,我偶然发现了许多这些问题,并且看到一般建议是使用外部工具(BeanShell,Groovy ......)而感到失望.在亚当潘德对这个问题的回答是为了最有帮助的,至少弄清楚相关的关键字.但即使通过咨询更多的外部资源(比如来自Java2s的一个例子),我也很难实现仅使用API 实现一个或多个Java类(实际上有效)的纯内存编译JavaCompiler
.
因此,这里有一个示例,显示了在运行时,当源代码以字符串形式给出时,在内存中编译一个或多个类的整个过程.它围绕一个小实用程序类构建,它只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; /** * An example showing how to use the RuntimeCompiler utility class */ public class RuntimeCompilerExample { public static void main(String[] args) throws Exception { simpleExample(); twoClassExample(); useLoadedClassExample(); } /** * Simple example: Shows how to add and compile a class, and then * invoke a static method on the loaded class. */ 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"); } /** * An example showing how to add two classes (where one refers to the * other), compile them, and invoke a static method on one of them */ 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"); } /** * An example that compiles and loads a class, and then uses an * instance of this class */ 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); } } /** * Utility class for compiling classes whose source code is given as * strings, in-memory, at runtime, using the JavaCompiler tools. */ class RuntimeCompiler { /** * The Java Compiler */ private final JavaCompiler javaCompiler; /** * The mapping from fully qualified class names to the class data */ private final Map classData; /** * A class loader that will look up classes in the {@link #classData} */ private final MapClassLoader mapClassLoader; /** * The JavaFileManager that will handle the compiled classes, and * eventually put them into the {@link #classData} */ private final ClassDataFileManager classDataFileManager; /** * The compilation units for the next compilation task */ private final List compilationUnits; /** * Creates a new RuntimeCompiler * * @throws NullPointerException If no JavaCompiler could be obtained. * This is the case when the application was not started with a JDK, * but only with a JRE. (More specifically: When the JDK tools are * not in the classpath). */ 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 (); } /** * Add a class with the given name and source code to be compiled * with the next call to {@link #compile()} * * @param className The class name * @param code The code of the class */ public void addClass(String className, String code) { String javaFileName = className + ".java"; JavaFileObject javaFileObject = new MemoryJavaSourceFileObject(javaFileName, code); compilationUnits.add(javaFileObject); } /** * Compile all classes that have been added by calling * {@link #addClass(String, String)} * * @return Whether the compilation succeeded */ 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; } /** * Obtain a class that was previously compiled by adding it with * {@link #addClass(String, String)} and calling {@link #compile()}. * * @param className The class name * @return The class. Returns null
if the compilation failed. */ public Class> getCompiledClass(String className) { return mapClassLoader.findClass(className); } /** * In-memory representation of a source JavaFileObject */ private static final class MemoryJavaSourceFileObject extends SimpleJavaFileObject { /** * The source code of the class */ private final String code; /** * Creates a new in-memory representation of a Java file * * @param fileName The file name * @param code The source code of the file */ 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; } } /** * A class loader that will look up classes in the {@link #classData} */ private class MapClassLoader extends ClassLoader { @Override public Class> findClass(String name) { byte[] b = classData.get(name); return defineClass(name, b, 0, b.length); } } /** * In-memory representation of a class JavaFileObject * @author User * */ private class MemoryJavaClassFileObject extends SimpleJavaFileObject { /** * The name of the class represented by the file object */ private final String className; /** * Create a new java file object that represents the specified class * * @param className THe name of the class */ 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); } } /** * A JavaFileManager that manages the compiled classes by passing * them to the {@link #classData} map via a ClassDataOutputStream */ private class ClassDataFileManager extends ForwardingJavaFileManager{ /** * Create a new file manager that delegates to the given file manager * * @param standardJavaFileManager The delegate file manager */ 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); } } /** * An output stream that is used by the ClassDataFileManager * to store the compiled classes in the {@link #classData} map */ private class ClassDataOutputStream extends OutputStream { /** * The name of the class that the received class data represents */ private final String className; /** * The output stream that will receive the class data */ private final ByteArrayOutputStream baos; /** * Creates a new output stream that will store the class * data for the class with the given name * * @param className The class name */ 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(); } } } /** * Utility methods not directly related to the RuntimeCompiler */ class MethodInvocationUtils { /** * Utility method to invoke the first static method in the given * class that can accept the given parameters. * * @param c The class * @param methodName The method name * @param args The arguments for the method call * @return The return value of the method call * @throws RuntimeException If either the class or a matching method * could not be found */ 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); } } /** * Utility method to find the first static method in the given * class that has the given name and can accept the given * arguments. Returns null
if no such method * can be found. * * @param c The class * @param methodName The name of the method * @param args The arguments * @return The first matching static method. */ 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; } /** * Returns whether the given arguments are assignable to the * respective types * * @param types The types * @param args The arguments * @return Whether the arguments are assignable */ private static boolean areAssignable(Class> types[], Object ...args) { if (types.length != args.length) { return false; } for (int i=0; itype = types[i]; if (arg != null && !type.isAssignableFrom(arg.getClass())) { return false; } } return true; } }
编辑回应评论:
为了编译外部JAR文件中包含的类,将JAR添加到classpath
调用应用程序中就足够了.然后,JavaCompiler
将接收此类路径以查找编译所需的类.
似乎有一些魔法涉及.至少,我还没有弄清楚背后的确切机制,只是用一个例子来测试它
并且旁注:当然,人们可以考虑对这个类进行字面上的任意扩展.我的目标是创建一个简单,独立,易于复制和匹配的示例,显示整个过程,甚至可能对某些应用程序模式"有用".
对于更复杂的功能,可以考虑相应地扩展这个类,或者看看,例如,在OpenHFT项目的Java-Runtime-Compiler中(我在写完这个答案后的几周内偶然发现了这个).它基本上在内部使用相同的技术,但是以更复杂的方式,并且还提供用于处理外部依赖性的类加载器的专用机制.
使用Groovy!
Binding binding = new Binding(); GroovyShell shell = new GroovyShell(binding); Object value = shell.evaluate("for (x=0; x<5; x++){println "Hello"}; return x");
你不能因为java是一种编译语言.
但是,您应该使用javax.script
api在运行时执行代码.JVM6附带Rhino(javascript解释器)avaliable via javax.script
.
http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html
有javax.script
兼容的java解释器(和bean shell)可用.
https://scripting.dev.java.net/
你可以使用像BeanShell这样的东西.
您可以使用BeanShell.
关键是bsh.Interpreter
.
这是一个简单的例子:
Interpreter interpreter = new Interpreter(); Object res = interpreter.eval("your expresion");
甚至可以定义整个类而不是单个表达式.