Java千百問_08 JDK詳細(008)_コードによるjavaファイルのコンパイル方法


クリックしてその他Java千百問
1、javaファイルをコードでどのようにコンパイルするか
コンパイラはコマンドラインツール(jdkが持参したコンパイラツールjavac、javacはここを参照:javacとは何か)ですが、APIを使用して呼び出すこともできます(一般的なIDEではこのAPIのセットを使用して独自のコンパイル機能をカプセル化します).コンパイラはJava言語仕様(The Java Language Specification,JLS)とJava仮想マシン仕様に従います(The Java Virtual Machine Specification,JVMS).Java 6の後、Javaコンパイラを操作するための標準パッケージが提供されています.これがjavax.toolsパッケージです.このパッケージのAPIおよびその他の補助パッケージを使用して、独自のコンパイラをカスタマイズできます.ToolProviderクラスのソースコードから、javax.toolsというパッケージのAPIは最終的にtools.jarのcom.sun.tを通じて行われていることがわかります.ools.JAvacパッケージはJavaコンパイラを呼び出します.
コードコンパイルjavaには大体次の3つの方法があり、これらの方法を活用してDIYが自分のコンパイラに属することができます.
JavaCompiler経由run()の最も簡単な使い方はJavaCompilerクラスを用いたrunメソッドで,最初の3つのパラメータはそれぞれ入力情報,出力情報,エラー情報,nullであればデフォルトはSystemである.in、System.out、System.err.最後のパラメータはjavac後のコマンドテキスト、例えばTestに入力.JAvaは、端末でjavac Testを実行することに等しい.java.
例:
public class Test {
 public static void main(String[] args) throws Exception {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int run = compiler.run(null, null, null, "-version");
        System.out.println("===" + run);
    }
}

実行結果(出力情報が指定されていないため、デフォルトではSystem.outに印刷されます):
javac 1.7.0_79 ===0
JavaCompiler経由getTask()コンパイルハードディスクのコードはJavaCompilerを使用する.run法は非常に簡単であるが,我々が必要とする情報をより効果的に得ることはできない.一般的にStandardJavaFileManagerクラス(jdk 6以上)を使用します.このクラスは入力、出力をよく制御し、DiagnosticListenerで診断情報を得ることができます.DiagnosticCollectorクラスはlistener(リスニング)の実装です.
具体的な例は次のとおりです.
public class Test {
public static void main(String[] args) throws Exception {
        Test.compiler();
}

public static void compiler() throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //   DiagnosticCollector  

        DiagnosticCollector diagnostics = new DiagnosticCollector();

        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);

        //        ,           JavaFileObject     
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays
                .asList("/Users/sunjie/Desktop/works/workspace/my-test/src/com/test/Test.java"));

        // options     
        Iterable<String> options = Arrays.asList("-d",
                "/Users/sunjie/Desktop/works/workspace/my-test/src/com/test/classes");//           ,javac         
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null,
                compilationUnits);

        //      
        boolean success = task.call();
        fileManager.close();
        System.out.println((success) ? "    " : "    ");

        //     
        for (Object object : diagnostics.getDiagnostics()) {
            Diagnostic diagnostic = (Diagnostic) object;
            System.out.printf("Code: %s%n" + "Kind: %s%n" + "Position: %s%n" + "Start Position: %s%n"
                    + "End Position: %s%n" + "Source: %s%n" + "Message: %s%n", diagnostic.getCode(),
                    diagnostic.getKind(), diagnostic.getPosition(), diagnostic.getStartPosition(),
                    diagnostic.getEndPosition(), diagnostic.getSource(), diagnostic.getMessage(null));
        }
}
}

実行結果は次のとおりです.
コンパイル成功
対応する経路でcom/test/Testが発見される.classファイル(Testのパッケージはpackage com.testなので、対応するディレクトリの下で自動的にcom/test/パスが生成されます).
JavaCompilerでgetTask()コンパイルメモリ内のコードJavaCompilerは、ハードディスク上のJavaファイルだけでなく、メモリ内のJavaコードをコンパイルしてreflectionを使用して実行することもできます.JavaSourceFromStringクラスを作成できます.このクラスでJavaソースコードを入力できます.
具体的な例は次のとおりです.
public class Test {

    public static void main(String[] args) throws Exception {
        Test.compiler2();
    }
 public static void compiler2() throws IOException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector diagnostics = new DiagnosticCollector();

        //     StringWriter ,   Java  
        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);
        //    Java  
        out.println("public class HelloWorld {");
        out.println(" public static void main(String args[]) {");
        out.println(" System.out.println(\"Hello, World\");");
        out.println(" }");
        out.println("}");
        out.close();

        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
        //          :HelloWorld
        SimpleJavaFileObject file = (new Test()).new JavaSourceFromString("HelloWorld", writer.toString());
        Iterable compilationUnits = Arrays.asList(file);
        // options     
        Iterable<String> options = Arrays.asList("-d",
                "/Users/sunjie/Desktop/works/workspace/my-test/src/com/test/classes");//           ,javac         
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null,
                compilationUnits);

        boolean success = task.call();
        System.out.println((success) ? "    " : "    ");
    }

    //         JavaSourceFromString 
    class JavaSourceFromString extends SimpleJavaFileObject {

        final String code;

        JavaSourceFromString(String name, String code) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.code = code;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }
}

実行結果は次のとおりです.
コンパイル成功
対応するパスの下にハローワールドがあります.classファイル.