ネイティブ・イメージの動的プロキシ
java.lang.reflect.Proxy
によって実装されるJava動的プロキシは、java.lang.reflect.InvocationHandler
を介してすべてのメソッド呼出しをルーティングすることでオブジェクト・レベルのアクセス制御を可能にするメカニズムです。動的プロキシ・クラスは、インタフェースのリストから生成されます。
ネイティブ・イメージは、実行時にバイトコードを生成および解釈するための機構を備えていません。したがって、すべての動的プロキシ・クラスをネイティブ・イメージのビルド時に生成する必要があります。
Javaリソースおよびその他の動的機能の構成支援に関するガイドも参照してください。
自動検出
ネイティブ・イメージでは、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};
静的分析は、動的プロキシ・クラスの定義に最もよく使用されるコード・パターンに対応しています。分析でインタフェース配列を検出できない例外的なケースに対応するために、手動の動的プロキシ構成メカニズムも用意されています。
手動構成
動的プロキシ・クラスは、それらによって実装されるインタフェースのリストを指定することで、ネイティブ・イメージのビルド時に生成できます。ネイティブ・イメージには、このために-H:DynamicProxyConfigurationFiles=<comma-separated-config-files>
および-H:DynamicProxyConfigurationResources=<comma-separated-config-resources>
の2つのオプションが用意されています。これらのオプションでは、構造が完全修飾インタフェース名の配列の配列であるJSONファイルが受け入れられます。たとえば:
[
{ "interfaces": [ "java.lang.AutoCloseable", "java.util.Comparator" ] },
{ "interfaces": [ "java.util.Comparator" ] },
{ "interfaces": [ "java.util.List" ] }
]
指定するプロキシ・インタフェースの順序は重要です: Proxy
クラスに対する2つのリクエストで、インタフェースの組合せが同じでも順序が異なる場合は、2つの異なる動作が発生します。詳細は、Proxy Class
javadocを参照してください。
java.lang.reflect.Proxy
APIでは、ユーザー指定のインタフェースを実装しない動的プロキシを作成することもできます。この場合、生成された動的プロキシ・クラスではjava.lang.reflect.Proxy
のみが実装されます。
静的イニシャライザの動的プロキシ・クラス
静的イニシャライザで定義された動的プロキシ・クラスとそのインスタンスには、特別な処理を行わなくても実行時にアクセスできます。これが可能なのは、静的イニシャライザがネイティブ・イメージのビルド時に実行されるためです。たとえば、次のものは機能します:
private final static Comparator proxyInstance;
private final static Class<?> proxyClass;
static {
ClassLoader classLoader = ProxyFactory.class.getClassLoader();
InvocationHandler handler = new ProxyInvocationHandler();
Class<?>[] interfaces = {java.util.Comparator.class};
proxyInstance = (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
proxyClass = Proxy.getProxyClass(classLoader, interfaces);
}