2

Java Compiler API を使用してメモリ内クラスをコンパイルしています。つまり、クラスはバイトコードにコンパイルされ (ディスクに保存された .classes ファイルはありません)、バイトコードを再構築することによってロードされます。

場合によっては、メモリ内でコンパイルされた別のクラスに依存するクラスをコンパイルする必要があります。例: クラス A をコンパイルしてから、クラス A に依存するクラス B をコンパイルします。

これを解決するために、コンパイラ API の getTask メソッドで必要なコンパイル ユニットとしてクラス A とクラス B の両方を渡します。

ただし、既にコンパイルされているクラス A を再コンパイルする必要があるため、このソリューションは本当に好きではありません。

これを回避する方法はありますか?

編集: このリンクから解決策を見つけました: http://www.ibm.com/developerworks/java/library/j-jcomp/index.html

4

4 に答える 4

2

はい、適切に実装する限り、これは完全に可能ですForwardingJavaFileManager。最も重要な 2 つのメソッドは、inferBinaryName()list()です。これら 2 つを適切に設定すると、コンパイラは以前にコンパイルしたクラスを解決できるようになります。

inferBinaryName()クラスの単純な名前を返さなければなりません(たとえば、 の推測されたバイナリ名はcom.test.Testただ になりますTest)。これが私の実装です(のサブクラスJavaFileObjectは呼び出されInAppJavaFileObjectます):

@Override
public String inferBinaryName(Location location, JavaFileObject javaFileObject) {

    if(location == StandardLocation.CLASS_PATH && javaFileObject instanceof InAppJavaFileObject) {
        return StringUtils.substringBeforeLast(javaFileObject.getName(), ".java");
    }

    return super.inferBinaryName(location, javaFileObject);
}

最後から「.java」を取り除いていることに注意してください。を構築するときJavaFileObject、ファイル名は「.java」で終わる必要がありますが、後で接尾辞を削除しないと、コンパイラはクラスを見つけられません。

list()デリゲート ファイル マネージャーとうまく連携するように注意する必要があるため、もう少し複雑です。私の実装では、完全修飾クラス名からサブクラスへのマップを保持しており、JavaFileObjectそれを繰り返し処理できます。

@Override
public Iterable<JavaFileObject> list(Location action, String pkg, Set<JavaFileObject.Kind> kind, boolean recurse) throws IOException {

    Iterable<JavaFileObject> superFiles = super.list(action, pkg, kind, recurse);

    // see if there's anything in our cache that matches the criteria.
    if(action == StandardLocation.CLASS_PATH && (kind.contains(JavaFileObject.Kind.CLASS) || kind.contains(JavaFileObject.Kind.SOURCE))) {

        List<JavaFileObject> ourFiles = new ArrayList<JavaFileObject>();
        for(Map.Entry<String,InAppJavaFileObject> entry : files.entrySet()) {
            String className = entry.getKey();
            if(className.startsWith(pkg) && ("".equals(pkg) || pkg.equals(className.substring(0, className.lastIndexOf('.'))))) {
                ourFiles.add(entry.getValue());
            }
        }

        if(ourFiles.size() > 0) {
            for(JavaFileObject javaFileObject : superFiles) {
                ourFiles.add(javaFileObject);
            }

            return ourFiles;
        }
    }

    // nothing found in our hash map that matches the criteria...  return
    // whatever super came up with.
    return superFiles;
}

これらのメソッドを適切に実装すると、残りは機能します。楽しみ!

于 2011-12-07T13:07:12.293 に答える
1

これは、なぜ最初にクラス A を個別にコンパイルする必要があるのか​​という明らかな疑問につながります。一度にすべてをコンパイルしないのはなぜですか?

于 2010-11-29T10:02:54.980 に答える
0

両方のクラスをコンパイルすることは避けられないと思います。実際、両方をコンパイルしないと、バイナリ互換性の問題、または誤ったインライン定数の問題が発生する可能性があります。

これは基本的に、コマンドラインから一方のクラスをコンパイルし、もう一方のクラスをコンパイルしない場合に発生する問題と同じです。

しかし、正直なところ、私はそのようにコンパイルを最適化しようとすることを心配しません。(そして、アプリケーションが一方のクラスを動的にコンパイルし、もう一方のクラスをコンパイルできないようにする必要がある場合は、おそらく設計上の重大な問題があります。)

于 2010-11-29T11:32:43.300 に答える
0

ファイルの変更時刻と (メモリ内で) コンパイルされたバイト コードを維持する場合はどうすればよいでしょうか?

于 2010-11-29T10:25:59.707 に答える