当前位置:  开发笔记 > 编程语言 > 正文

将字符串转换为代码

如何解决《将字符串转换为代码》经验,为你挑选了6个好方法。

我想知道是否有任何方法可以将StringJava 转换为Java可编译代码.

我有一个比较表达式保存在数据库字段中.我想从数据库中检索它,然后在条件结构中进行评估.

有没有办法做到这一点?



1> Adam Paynter..:

如果您使用的是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 diagnosticListener = null;
Iterable options = null;
Iterable classes = null;
Iterable compilationUnits = new ArrayList();
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表达式存储在数据库字段中,引用ab.


好吧,如果你想成为TheDailyWTF的特色,那就去吧.
您是否暗示要在TheDailyWTF上展示Java Server Pages?哦......是的......也许他们应该......我讨厌和他们打交道是有充分理由的:P

2> Marco13..:

作为一个字符串,给出了如何以编程方式编译Java代码的问题是问相当频繁,并以各种形式,有时指的是存储在一个代码数据库或通过输入用户.当我搜索有关这方面的信息时,我偶然发现了许多这些问题,并且看到一般建议是使用外部工具(BeanShell,Groovy ......)而感到失望.在亚当潘德对这个问题的回答是为了最有帮助的,至少弄清楚相关的关键字.但即使通过咨询更多的外部资源(比如来自J​​ava2s的一个例子),我也很难实现仅使用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; i type = types[i];
            if (arg != null && !type.isAssignableFrom(arg.getClass()))
            {
                return false;
            }
        }
        return true;
    }

}

编辑回应评论:

为了编译外部JAR文件中包含的类,将JAR添加到classpath调用应用程序中就足够了.然后,JavaCompiler将接收此类路径以查找编译所需的类.

似乎有一些魔法涉及.至少,我还没有弄清楚背后的确切机制,只是用一个例子来测试它

并且旁注:当然,人们可以考虑对这个类进行字面上的任意扩展.我的目标是创建一个简单,独立,易于复制和匹配的示例,显示整个过程,甚至可能对某些应用程序模式"有用".

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



3> 小智..:

使用Groovy!

Binding binding = new Binding();
GroovyShell shell = new GroovyShell(binding);
Object value = shell.evaluate("for (x=0; x<5; x++){println "Hello"}; return x");


shell.evaluate可以用来创建数据库和其他连接吗?如果我要评估的代码具有创建jdbc连接的代码,那么它也会起作用吗?

4> alamar..:

你不能因为java是一种编译语言.

但是,您应该使用javax.scriptapi在运行时执行代码.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/


我认为这个答案是错误的 - 生成Java源代码,编译它,并且绝对可以使用它(JSP是完美的证明.)这不是一个好主意或一个好的设计决策,但它可以做到.
这个答案完全错了.您可以使用一些字节码操作工具来创建新类并加载它.javassist的一个例子:http://davidwinterfeldt.blogspot.com/2009/02/genearting-bytecode.html

5> Kris..:

你可以使用像BeanShell这样的东西.



6> vitali_y..:

您可以使用BeanShell.
关键是bsh.Interpreter.

这是一个简单的例子:

Interpreter interpreter = new Interpreter();
Object res = interpreter.eval("your expresion");

甚至可以定义整个类而不是单个表达式.

推荐阅读
雨天是最美
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有