利用javax.tools动态编译执行java代码

 本文永久地址:https://my.oschina.net/bysu/blog/1552933

inkfish原创,请勿商业性质转载,转载请注明来源(http://blog.csdn.net/inkfish )。

  参考:使用 javax.tools 创建动态应用程序

 

  javax.tools 包是一种添加到 Java SE 6 的标准 API,可以实现 Java 源代码编译,使您能够添加动态功能来扩展静态应用程序。本文将探查javax.tools包中提供的主要类,以Java表达式表示计算一个数值函数y=x*x+x。更多详情请参考《使用 javax.tools 创建动态应用程序》和javax.tools API docs 。

 

complier.CharSequenceCompiler源码:

package complier;  
import java.nio.charset.Charset;  
import java.util.ArrayList;  
import java.util.Collection;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.Map.Entry;  
import javax.tools.DiagnosticCollector;  
import javax.tools.JavaCompiler;  
import javax.tools.JavaFileObject;  
import javax.tools.StandardLocation;  
import javax.tools.ToolProvider;  
import javax.tools.JavaCompiler.CompilationTask;  
/** 
 * 编译{@link CharSequence}形式的源码,并实例化,返回一个实例。<br> 
 * 用法示例(以编译MyInterface的一个实现类为例): 
 *  
 * <pre> 
 * MyInterface instance = null; 
 * JavaStringCompiler<MyInterface> compiler = new JavaStringCompiler<MyInterface>(null, null); 
 * try { 
 *  Class<MyInterface> newClass = compiler.compile("com.mypackage.NewClass", 
 *          stringContaininSourceForNewClass, new Class<?>[] { MyInterface.class }); 
 *  instance = newClass.newInstance(); 
 * } catch (JavaStringCompilerException ex) { 
 *  ex.printStackTrace(); 
 * } catch (IllegalAccessException ex) { 
 *  ex.printStackTrace(); 
 * } 
 * instance.someOperation(someArgs); 
 * </pre> 
 */  
public class CharSequenceCompiler<T> {  
    /** 真正使用的编译器 */  
    private final JavaCompiler compiler;  
    private final ClassLoaderImpl classLoader;  
    /** 保存编译器编译中的诊断信息 */  
    private DiagnosticCollector<JavaFileObject> diagnostics;  
    private final FileManagerImpl javaFileManager;  
    /** 编译参数 */  
    private final List<String> options;  
    /** 
     * 构造一个新的实例,该实例持有指定的classloader 
     *  
     * @param loader 
     *            应用的{@link ClassLoader} 
     * @param options 
     *            编译器的编译参数,具体可参考javac编译参数 
     * @throws IllegalStateException 
     *             如果java编译器不能正确载入则抛出异常 
     */  
    public CharSequenceCompiler(ClassLoader loader, Collection<String> options) {  
        compiler = ToolProvider.getSystemJavaCompiler();  
        if (compiler == null) {  
            throw new IllegalStateException("系统java编译器无法找到,请确认类路径中已经包含tools.jar(注:JDK 6中默认自带,JRE 6中默认不带)。");  
        }  
        if (loader == null) {  
            classLoader = new ClassLoaderImpl(this.getClass().getClassLoader());  
        } else {  
            classLoader = new ClassLoaderImpl(loader);  
        }  
        this.options = new ArrayList<String>();  
        if (options != null) {  
            this.options.addAll(options);  
        }  
        diagnostics = new DiagnosticCollector<JavaFileObject>();  
        javaFileManager = new FileManagerImpl(compiler.getStandardFileManager(diagnostics, null, Charset  
                .forName(Utils.ENCODING)), classLoader);  
    }  
    /** 
     * 编译多个Java类的源码 
     *  
     * @param classes 
     *            key为类的完全限定名,value为对应的源码。 
     * @return 编译后的类 
     * @throws CharSequenceCompilerException 
     */  
    public synchronized Map<String, Class<T>> compile(Map<String, CharSequence> classes)  
            throws CharSequenceCompilerException {  
        //准备待编译文件  
        List<JavaFileObject> sources = new ArrayList<JavaFileObject>();  
        for (Entry<String, CharSequence> entry : classes.entrySet()) {  
            String qualifiedClassName = entry.getKey();  
            CharSequence javaSource = entry.getValue();  
            if (javaSource != null) {  
                int dotPos = qualifiedClassName.lastIndexOf('.');  
                String className = dotPos == -1 ? qualifiedClassName : qualifiedClassName  
                        .substring(dotPos + 1);  
                String packageName = dotPos == -1 ? "" : qualifiedClassName.substring(0, dotPos);  
                JavaFileObjectImpl source = new JavaFileObjectImpl(className, javaSource);  
                sources.add(source);  
                javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName, className  
                        + ".java", source);  
            }  
        }  
        //编译代码  
        CompilationTask task = compiler.getTask(null, javaFileManager, diagnostics, options, null, sources);  
        Boolean result = task.call();  
        //返回编译结果  
        if ((result == null) || !result.booleanValue()) {  
            throw new CharSequenceCompilerException("Compilation failed.", classes.keySet(), diagnostics);  
        }  
        try {  
            Map<String, Class<T>> compiled = new HashMap<String, Class<T>>();  
            for (String qualifiedClassName : classes.keySet()) {  
                compiled.put(qualifiedClassName, loadClass(qualifiedClassName));  
            }  
            return compiled;  
        } catch (ClassNotFoundException ex) {  
            throw new CharSequenceCompilerException(classes.keySet(), ex, diagnostics);  
        } catch (IllegalArgumentException ex) {  
            throw new CharSequenceCompilerException(classes.keySet(), ex, diagnostics);  
        } catch (SecurityException ex) {  
            throw new CharSequenceCompilerException(classes.keySet(), ex, diagnostics);  
        }  
    }  
    /** 
     * 编译一个Java类。 
     *  
     * @param qualifiedClassName 
     *            类的完全限定名。 
     * @param javaSource 
     *            编译的java类完整的源码。 
     * @param types 
     *            0或多个类,用以检验被编译的类能否转换成这些类中任何一个。 
     * @return 编译后的类 
     * @throws CharSequenceCompilerException 
     *             如果类无法被编译则抛出异常。 
     * @throws ClassCastException 
     *             如果编译后的类无法转换成types中的任何一种类型,则抛出异常。 
     */  
    public synchronized Class<T> compile(String qualifiedClassName, CharSequence javaSource,  
            Class<?>... types) throws CharSequenceCompilerException, ClassCastException {  
        diagnostics = new DiagnosticCollector<JavaFileObject>();  
        Map<String, CharSequence> classes = new HashMap<String, CharSequence>(1);  
        classes.put(qualifiedClassName, javaSource);  
        Map<String, Class<T>> compiled = compile(classes);  
        Class<T> newClass = compiled.get(qualifiedClassName);  
        for (Class<?> type : types) {  
            if (!type.isAssignableFrom(newClass)) {  
                throw new ClassCastException(type.getName());  
            }  
        }  
        return newClass;  
    }  
    /** 载入Java类。 */  
    @SuppressWarnings("unchecked")  
    private Class<T> loadClass(final String qualifiedClassName) throws ClassNotFoundException {  
        return (Class<T>) classLoader.loadClass(qualifiedClassName);  
    }  
}  

 

complier.CharSequenceCompilerException源码:

package complier;  
import java.util.Collection;  
import java.util.Collections;  
import java.util.HashSet;  
import java.util.Set;  
import javax.tools.DiagnosticCollector;  
import javax.tools.JavaFileObject;  
@SuppressWarnings("serial")  
public class CharSequenceCompilerException extends Exception {  
    /** 所有被编译的类的完整类名 */  
    private Set<String> classNames;  
    transient private DiagnosticCollector<JavaFileObject> diagnostics;  
    public CharSequenceCompilerException(Set<String> qualifiedClassNames, Throwable cause,  
            DiagnosticCollector<JavaFileObject> diagnostics) {  
        super(cause);  
        setClassNames(qualifiedClassNames);  
        setDiagnostics(diagnostics);  
    }  
    public CharSequenceCompilerException(String message, Set<String> qualifiedClassNames,  
            DiagnosticCollector<JavaFileObject> diagnostics) {  
        super(message);  
        setClassNames(qualifiedClassNames);  
        setDiagnostics(diagnostics);  
    }  
    public CharSequenceCompilerException(String message, Set<String> qualifiedClassNames, Throwable cause,  
            DiagnosticCollector<JavaFileObject> diagnostics) {  
        super(message, cause);  
        setClassNames(qualifiedClassNames);  
        setDiagnostics(diagnostics);  
    }  
    /** @return 返回编译出问题的类的全名称 */  
    public Collection<String> getClassNames() {  
        return Collections.unmodifiableSet(classNames);  
    }  
    /** 得到异常的诊断信息 */  
    public DiagnosticCollector<JavaFileObject> getDiagnostics() {  
        return diagnostics;  
    }  
    private void setClassNames(Set<String> qualifiedClassNames) {  
        classNames = new HashSet<String>(qualifiedClassNames);  
    }  
    private void setDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) {  
        this.diagnostics = diagnostics;  
    }  
}  

 

complier.ClassLoaderImpl源码:

package complier;  
import java.io.ByteArrayInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.util.Collection;  
import java.util.Collections;  
import java.util.HashMap;  
import java.util.Map;  
import javax.tools.JavaFileObject;  
import org.apache.commons.io.IOUtils;  
/** {@link ClassLoader}的一个实现,它map类名和JavaFileObjectImpl的实例。本类在{@link CharSequenceCompiler}和{@link FileManagerImpl}中被使用。 */  
final class ClassLoaderImpl extends ClassLoader {  
    private final Map<String, JavaFileObjectImpl> classes = new HashMap<String, JavaFileObjectImpl>();  
    ClassLoaderImpl(final ClassLoader parentClassLoader) {  
        super(parentClassLoader);  
    }  
    @Override  
    public InputStream getResourceAsStream(final String name) {  
        if (name.endsWith(".class")) {  
            String qualifiedClassName = name.substring(0, name.length() - ".class".length())  
                    .replace('/', '.');  
            JavaFileObjectImpl file = classes.get(qualifiedClassName);  
            if (file != null) {  
                try {  
                    return new ByteArrayInputStream(IOUtils.toByteArray(file.openInputStream()));  
                } catch (IOException ex) {  
                }  
            }  
        }  
        return super.getResourceAsStream(name);  
    }  
    protected void add(final String qualifiedClassName, final JavaFileObjectImpl javaFile) {  
        classes.put(qualifiedClassName, javaFile);  
    }  
    /** @return 返回不可变的Collection,含有所有持有的{@link JavaFileObject}对象 */  
    protected Collection<JavaFileObjectImpl> files() {  
        return Collections.unmodifiableCollection(classes.values());  
    }  
    @Override  
    protected Class<?> findClass(final String qualifiedClassName) throws ClassNotFoundException {  
        JavaFileObject file = classes.get(qualifiedClassName);  
        if (file != null) {  
            try {  
                byte[] bytes = IOUtils.toByteArray(file.openInputStream());  
                return defineClass(qualifiedClassName, bytes, 0, bytes.length);  
            } catch (IOException ex) {  
            }  
        }  
        // Workaround in Java 6. see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6434149  
        try {  
            Class<?> c = Class.forName(qualifiedClassName);  
            return c;  
        } catch (ClassNotFoundException nf) {  
        }  
        return super.findClass(qualifiedClassName);  
    }  
    @Override  
    protected synchronized Class<?> loadClass(final String name, final boolean resolve)  
            throws ClassNotFoundException {  
        return super.loadClass(name, resolve);  
    }  
}  

complier.FileManagerImpl源码:

package complier;  
import java.io.IOException;  
import java.net.URI;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.Map;  
import java.util.Set;  
import javax.tools.FileObject;  
import javax.tools.ForwardingJavaFileManager;  
import javax.tools.JavaFileManager;  
import javax.tools.JavaFileObject;  
import javax.tools.StandardLocation;  
import javax.tools.JavaFileObject.Kind;  
/** 
 * {@link JavaFileManager}的一个实例,用于管理Java源代码和byte code。<br> 
 * 所有的源码以{@link CharSequence}的形式保存在内存中,byte code以byte数组形式存放在内存中。 
 */  
final class FileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {  
    private final ClassLoaderImpl classLoader;  
    private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>();  
    FileManagerImpl(JavaFileManager fileManager, ClassLoaderImpl classLoader) {  
        super(fileManager);  
        this.classLoader = classLoader;  
    }  
    @Override  
    public ClassLoader getClassLoader(JavaFileManager.Location location) {  
        return classLoader;  
    }  
    @Override  
    public FileObject getFileForInput(Location location, String packageName, String relativeName)  
            throws IOException {  
        FileObject o = fileObjects.get(uri(location, packageName, relativeName));  
        if (o != null) {  
            return o;  
        }  
        return super.getFileForInput(location, packageName, relativeName);  
    }  
    @Override  
    public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind,  
            FileObject outputFile) throws IOException {  
        JavaFileObjectImpl file = new JavaFileObjectImpl(qualifiedName, kind);  
        classLoader.add(qualifiedName, file);  
        return file;  
    }  
    @Override  
    public String inferBinaryName(Location loc, JavaFileObject file) {  
        String result;  
        if (file instanceof JavaFileObjectImpl) {  
            result = file.getName();  
        } else {  
            result = super.inferBinaryName(loc, file);  
        }  
        return result;  
    }  
    @Override  
    public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds,  
            boolean recurse) throws IOException {  
        Iterable<JavaFileObject> result = super.list(location, packageName, kinds, recurse);  
        ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>();  
        if ((location == StandardLocation.CLASS_PATH) && kinds.contains(JavaFileObject.Kind.CLASS)) {  
            for (JavaFileObject file : fileObjects.values()) {  
                if ((file.getKind() == Kind.CLASS) && file.getName().startsWith(packageName)) {  
                    files.add(file);  
                }  
            }  
            files.addAll(classLoader.files());  
        } else if ((location == StandardLocation.SOURCE_PATH) && kinds.contains(JavaFileObject.Kind.SOURCE)) {  
            for (JavaFileObject file : fileObjects.values()) {  
                if ((file.getKind() == Kind.SOURCE) && file.getName().startsWith(packageName)) {  
                    files.add(file);  
                }  
            }  
        }  
        for (JavaFileObject file : result) {  
            files.add(file);  
        }  
        return files;  
    }  
    void putFileForInput(StandardLocation location, String packageName, String relativeName,  
            JavaFileObject file) {  
        fileObjects.put(uri(location, packageName, relativeName), file);  
    }  
    private URI uri(Location location, String packageName, String relativeName) {  
        return Utils.toURI(new StringBuilder(location.getName()).append('/').append(packageName).append('/')  
                .append(relativeName).toString());  
    }  
}  

 

complier.JavaFileObjectImpl源码:

package complier;  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.OutputStream;  
import java.io.OutputStreamWriter;  
import java.io.Writer;  
import javax.tools.FileObject;  
import javax.tools.JavaFileObject;  
import javax.tools.SimpleJavaFileObject;  
/** 
 * {@link FileObject}和{@link JavaFileObject}的一个实现,它能持有java源代码或编译后的class。这个类可以用于: 
 * <ol> 
 * <li>存放需要传递给编译器的源码,这时使用的是{@link JavaFileObjectImpl#JavaFileObjectImpl(String, CharSequence)}构造器。</li> 
 * <li>存放编译器编译完的byte code,这是使用的是{@link JavaFileObjectImpl#JavaFileObjectImpl(String, JavaFileObject.Kind)}</li> 
 * </ol> 
 */  
final class JavaFileObjectImpl extends SimpleJavaFileObject {  
    /** 如果kind == CLASS, 存储byte code,可以通过{@link #openInputStream()}得到 */  
    private ByteArrayOutputStream byteCode;  
    /** 如果kind == SOURCE, 存储源码 */  
    private final CharSequence source;  
    /** 
     * 创建持有源码的实例 
     *  
     * @param baseName 
     *            the base name 
     * @param source 
     *            the source code 
     */  
    JavaFileObjectImpl(final String baseName, final CharSequence source) {  
        super(Utils.toURI(baseName + ".java"), Kind.SOURCE);  
        this.source = source;  
    }  
    /** 
     * 创建持有二进制byte code的实例 
     *  
     * @param name 
     *            the file name 
     * @param kind 
     *            the kind of file 
     */  
    JavaFileObjectImpl(final String name, final Kind kind) {  
        super(Utils.toURI(name), kind);  
        source = null;  
    }  
    @Override  
    public CharSequence getCharContent(final boolean ignoreEncodingErrors)  
            throws UnsupportedOperationException {  
        if (source == null) {  
            throw new UnsupportedOperationException("getCharContent()");  
        }  
        return source;  
    }  
    @Override  
    public InputStream openInputStream() {  
        return new ByteArrayInputStream(byteCode.toByteArray());  
    }  
    @Override  
    public OutputStream openOutputStream() {  
        return (byteCode = new ByteArrayOutputStream());  
    }  
    @Override  
    public Writer openWriter() throws IOException {  
        return new OutputStreamWriter(openOutputStream(), Utils.ENCODING);  
    }  
}  

 

complier.Utils源码:

package complier;  
import java.net.URI;  
import java.net.URISyntaxException;  
import java.nio.charset.Charset;  
public abstract class Utils {  
    public static final String ENCODING = Charset.defaultCharset().name();  
    /** 把String转换成URI,如果转换异常不抛出URISyntaxException,而直接抛出RuntimeException。 */  
    static URI toURI(String name) {  
        try {  
            return new URI(name);  
        } catch (URISyntaxException e) {  
            throw new RuntimeException(e);  
        }  
    }  
}  

  以上代码为complier包中所有类,它对外暴露的主要方法是:CharSequenceCompiler<T>.compile(String qualifiedClassName, CharSequence javaSource, Class<?>... types) throws CharSequenceCompilerException, ClassCastException,通过它来动态编译字符串形式表示的java源代码。除此之外,包中其他方类和方法尽量使用默认访问权限,以避免他人误用以及隐藏实现细节。

  下面是测试package中的内容:

 

complier.test.Function源码:定义了一个接口,动态编译的所有类实现这个接口。

package complier.test;  
public interface Function {  
    double doFunction(double x);  
}  

complier.test.Function实现类的模板,方便类的生成。

package $packageName;  
import static java.lang.Math.*;  
public class $className implements complier.test.Function {  
    @Override  
    public double doFunction(double x){  
        return $expression;  
    }  
}  

complier.test.ExpressionCal源码:里面含有一个静态测试类ExpressionCal$Tester。

package complier.test;  
import java.io.IOException;  
import java.text.DecimalFormat;  
import java.util.Arrays;  
import java.util.Random;  
import javax.script.Bindings;  
import javax.script.Compilable;  
import javax.script.CompiledScript;  
import javax.script.ScriptEngine;  
import javax.script.ScriptEngineManager;  
import javax.script.ScriptException;  
import javax.script.SimpleBindings;  
import javax.tools.Diagnostic;  
import javax.tools.DiagnosticCollector;  
import javax.tools.JavaFileObject;  
import org.apache.commons.io.IOUtils;  
import org.apache.commons.lang.exception.ExceptionUtils;  
import complier.CharSequenceCompiler;  
import complier.CharSequenceCompilerException;  
import complier.Utils;  
public class ExpressionCal {  
    /** 包名前缀 */  
    private static final String PACKAGE_NAME = "javaxtools.compiler.test.runtime";  
    public static class Tester {  
        public static void main(String[] args) throws ScriptException {  
            //第一遍测试  
            test();  
            System.out.println("------Test Twice------/n");  
            test();  
        }  
        public static void test() throws ScriptException {  
            DecimalFormat df = new DecimalFormat("0.00");  
            int loop = 10 * 10000 - 1;  
            String exp = "x*x+x";  
            double d = new Random().nextDouble() * 100;  
            long start;  
            //直接计算  
            start = System.nanoTime();  
            for (int i = 0; i < loop; i++) {  
                @SuppressWarnings("unused")  
                double a = d * d + d;  
            }  
            System.out.printf(exp.replace("x", df.format(d)) + "=%2.4f/n", d * d + d);  
            System.out.printf("Time of direct cal %d loops: %10.2f微秒./n/n", loop + 1,  
                    (System.nanoTime() - start) / 1000d);  
            //编译源码并计算  
            start = System.nanoTime();  
            Function func = new ExpressionCal().newFunction(exp);  
            System.out.printf("Java src complain time: %10.2f微秒, /t", (System.nanoTime() - start) / 1000d);  
            start = System.nanoTime();  
            for (int i = 0; i < loop; i++) {  
                func.doFunction(d);  
            }  
            System.out.printf(exp.replace("x", df.format(d)) + "=%2.4f/n", func.doFunction(d));  
            System.out.printf("Complained source %d loops: %10.2f微秒./n/n", loop + 1,  
                    (System.nanoTime() - start) / 1000d);  
            //内置Javascript计算  
            start = System.nanoTime();  
            ScriptEngine se = new ScriptEngineManager().getEngineByName("ECMAScript");  
            CompiledScript script = ((Compilable) se).compile("var x;" + exp);  
            System.out.printf("JS complain time: %10.2f微秒, /t", (System.nanoTime() - start) / 1000d);  
            start = System.nanoTime();  
            Bindings binding = new SimpleBindings();  
            for (int i = 0; i < loop; i++) {  
                binding.put("x", d);  
                script.eval(binding);  
            }  
            binding.put("x", d);  
            System.out.printf(exp.replace("x", df.format(d)) + "=%2.4f/n", script.eval(binding));  
            System.out.printf("Javascript %d loops: %10.2f微秒./n", loop + 1,  
                    (System.nanoTime() - start) / 1000d);  
        }  
    }  
    /** 类名后缀 */  
    private int classNameSuffix = 0;  
    /** 随机数生成器,用于生成随机的包名和类名 */  
    private static final Random random = new Random();  
    /** 字符串形式的Java源文件内容 */  
    private String template;  
    private static final String TEMPLATE_NAME = "Function.java.template";  
    private static final String TARGET_VERSION = "1.6";  
    private final CharSequenceCompiler<Function> compiler = new CharSequenceCompiler<Function>(getClass()  
            .getClassLoader(), Arrays.asList(new String[] { "-target", TARGET_VERSION, "-encoding",  
            Utils.ENCODING }));  
    public Function newFunction(String expr) {  
        StringBuilder errStr = new StringBuilder();  
        Function result = null;  
        try {  
            //生成唯一的包名和类名  
            final String packageName = PACKAGE_NAME + digits();  
            final String className = "C_" + (classNameSuffix++) + digits();  
            final String qName = packageName + '.' + className;  
            //生成类的源码  
            final String source = fillTemplate(packageName, className, expr);  
            //编译源码  
            Class<Function> compiledFunction = compiler.compile(qName, source,  
                    new Class<?>[] { Function.class });  
            result = compiledFunction.newInstance();  
        } catch (CharSequenceCompilerException ex) {  
            errStr.append(log(ex.getDiagnostics()));  
            ex.printStackTrace();  
        } catch (InstantiationException ex) {  
            errStr.append(ExceptionUtils.getFullStackTrace(ex)).append("/n");  
            ex.printStackTrace();  
        } catch (IllegalAccessException ex) {  
            errStr.append(ExceptionUtils.getFullStackTrace(ex)).append("/n");  
            ex.printStackTrace();  
        } catch (IOException ex) {  
            errStr.append(ExceptionUtils.getFullStackTrace(ex)).append("/n");  
            ex.printStackTrace();  
        }  
        if (errStr.toString().trim().length() > 0) {  
            System.err.println(errStr.toString());  
        }  
        return result;  
    }  
    /** @return 返回以'_'开头的随机16进制字符串 */  
    private String digits() {  
        return '_' + Long.toHexString(random.nextLong());  
    }  
    /** 
     * 生成字符串形式的java源文件内容 
     *  
     * @param packageName 
     *            包名 
     * @param className 
     *            类名 
     * @param expression 
     *            表达式 
     * @return 字符串形式的java源文件内容 
     * @throws IOException 
     */  
    private String fillTemplate(String packageName, String className, String expression) throws IOException {  
        if (template == null) {  
            template = IOUtils.toString(Function.class.getResourceAsStream(TEMPLATE_NAME), Utils.ENCODING);  
        }  
        String source = template.replace("$packageName", packageName)//  
                .replace("$className", className)//  
                .replace("$expression", expression);  
        return source;  
    }  
    /** 记录{@link DiagnosticCollector}中的错误内容 */  
    private CharSequence log(final DiagnosticCollector<JavaFileObject> diagnostics) {  
        final StringBuilder msgs = new StringBuilder();  
        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {  
            msgs.append(diagnostic.getMessage(null)).append("/n");  
        }  
        return msgs;  
    }  
}  

 

ExpressionCal$Tester测试表达式x*x+x的运行,具体测试直接计算、java编译并计算、javascript编译并计算的效率,共测试三遍以观察其效率的变化,每一遍中每种方法又用for循环运行10万次计算,忽略打印输出的耗时。测试时间使用微秒为单位,精确度取决于System.nanoTime(),详见其java docs中的说明。测试结果如下:

44.53*44.53+44.53=2027.7451  
Time of direct cal 100000 loops:    6461.72微秒.  
Java src complain time:  604570.13微秒,   44.53*44.53+44.53=2027.7451  
Complained source 100000 loops:    5412.14微秒.  
JS complain time:   23354.09微秒,     44.53*44.53+44.53=2027.7451  
Javascript 100000 loops: 8671081.10微秒.  
------Test twice------  
7.67*7.67+7.67=66.4529  
Time of direct cal 100000 loops:     670.48微秒.  
Java src complain time:   44715.18微秒,   7.67*7.67+7.67=66.4529  
Complained source 100000 loops:    1397.38微秒.  
JS complain time:    2375.44微秒,     7.67*7.67+7.67=66.4529  
Javascript 100000 loops: 8493123.29微秒.  
------Test third times------  
74.34*74.34+74.34=5600.4535  
Time of direct cal 100000 loops:     572.14微秒.  
Java src complain time:   39487.42微秒,   74.34*74.34+74.34=5600.4535  
Complained source 100000 loops:    1375.04微秒.  
JS complain time:    1867.56微秒,     74.34*74.34+74.34=5600.4535  
Javascript 100000 loops: 8624124.85微秒.  

整理得到:

10万次计算
(单位:毫秒)
直接计算java编译并计算JS编译并计算
编译计算编译计算
第一遍6.46604.575.4123.358671.08
第二遍0.6744.711.42.388493.12
第三遍0.5739.491.381.878624.12

 

  可以看出,java直接计算速度超快,java编译并计算速度还是比直接计算慢1倍(不计编译时间),而JS的计算速度比直接计算慢4个数量级,简直惨不忍睹。第一次运行除JS计算外,均比较耗时,其中java第一次编译需要从磁盘上读取template文件,以后则均为内存操作。

  从测试结果看,如果需要运行一个固定的表达式,可以写死在Java程序中(废话),如果需要计算一个动态变化的表达式,如果计算次数较少(500次以下),JS较为划算,如果计算次数十分巨大,则需要考虑java编译并计算。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值