ネイティブ・イメージの動的プロキシ

java.lang.reflect.Proxyによって実装されるJava動的プロキシは、java.lang.reflect.InvocationHandlerを介してすべてのメソッド呼出しをルーティングすることでオブジェクト・レベルのアクセス制御を可能にするメカニズムです。動的プロキシ・クラスは、インタフェースのリストから生成されます。

ネイティブ・イメージは、実行時にバイトコードを生成および解釈するための機構を備えていません。したがって、すべての動的プロキシ・クラスをイメージのビルド時に生成する必要があります。

自動検出

ネイティブ・イメージでは、java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)およびjava.lang.reflect.Proxy.getProxyClass(ClassLoader, Class<?>[])のコールを検出する単純な静的分析が使用され、これにより、動的プロキシを自動的に定義するインタフェースのリストが決定されます。インタフェースのリストが決定すると、イメージのビルド時にプロキシ・クラスが生成され、ネイティブ・イメージ・ヒープに追加されます。動的プロキシ・クラスが生成されるだけでなく、java.lang.reflect.InvocationHandler引数を取得する生成済クラスのコンストラクタ、つまりjava.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)によってリフレクティブに起動されるコンストラクタがリフレクション用に登録され、それによって実行時の動的プロキシ・インスタンスの割当てが可能になります。

分析は、インタフェースのリストが定数配列または同じメソッドで割り当てられる配列から取得される状況に限定されます。たとえば、次のコード・スニペットでは、動的プロキシ・インタフェースを自動的に決定できます。

静的最終配列:

class ProxyFactory {

    private static final Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};

    static Comparator createProxyInstanceFromConstantArray() {
        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
        InvocationHandler handler = new ProxyInvocationHandler();
        return (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
    }
}

分析は、ソース・コードではなくコンパイラ・グラフに対して実行されることに注意してください。したがって、配列を宣言および移入する次の方法は、分析の観点からは同等です:

private static final Class<?>[] interfacesArrayPreInitialized = new Class<?>[]{java.util.Comparator.class};
private static final Class<?>[] interfacesArrayLiteral = {java.util.Comparator.class};
private static final Class<?>[] interfacesArrayPostInitialized = new Class<?>[1];
static {
    interfacesArrayPostInitialized[0] = java.util.Comparator.class;
}

ただし、Javaには不変の配列はありません。配列がstatic finalとして宣言されている場合でも、その内容は後で変更できます。ここで使用される単純な分析では、配列に対するそれ以上の変更は追跡されません。

新規配列:

class ProxyFactory {

    static Comparator createProxyInstanceFromNewArray() {
        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
        InvocationHandler handler = new ProxyInvocationHandler();
        Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
        return (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
    }
}

ノート: 定数配列と同様に、配列を宣言および移入する次の方法は、分析の観点からは同等です:

Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
Class<?>[] interfaces = new Class<?>[1];
interfaces[0] = Question.class;
Class<?>[] interfaces = {java.util.Comparator.class};

静的分析は、動的プロキシ・クラスの定義に最もよく使用されるコード・パターンに対応しています。分析でインタフェース配列を検出できない例外的なケースに対応するために、手動の動的プロキシ構成メカニズムも用意されています。