利用javax.tools動態編譯執行java代碼

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

inkfish原創,請勿商業性質轉載,轉載請註明來源(http://blog.csdn.net/inkfish )。html

  參考:使用 javax.tools 建立動態應用程序java

 

  javax.tools 包是一種添加到 Java SE 6 的標準 API,能夠實現 Java 源代碼編譯,使您可以添加動態功能來擴展靜態應用程序。本文將探查javax.tools包中提供的主要類,以Java表達式表示計算一個數值函數y=x*x+x。更多詳情請參考《使用 javax.tools 建立動態應用程序》和javax.tools API docs 。git

 

complier.CharSequenceCompiler源碼:express

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源碼:apache

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源碼:api

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源碼:app

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源碼:dom

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.46 604.57 5.41 23.35 8671.08
第二遍 0.67 44.71 1.4 2.38 8493.12
第三遍 0.57 39.49 1.38 1.87 8624.12

 

  能夠看出,java直接計算速度超快,java編譯並計算速度仍是比直接計算慢1倍(不計編譯時間),而JS的計算速度比直接計算慢4個數量級,簡直慘不忍睹。第一次運行除JS計算外,均比較耗時,其中java第一次編譯須要從磁盤上讀取template文件,之後則均爲內存操做。

  從測試結果看,若是須要運行一個固定的表達式,能夠寫死在Java程序中(廢話),若是須要計算一個動態變化的表達式,若是計算次數較少(500次如下),JS較爲划算,若是計算次數十分巨大,則須要考慮java編譯並計算。

相關文章
相關標籤/搜索