トレース・エージェントを使用したネイティブ・イメージの構成
Javaリフレクション、動的プロキシ・オブジェクト、JNIまたはクラス・パス・リソースを使用するJavaアプリケーションのネイティブ実行可能ファイルをビルドするには、native-image
ツールにJSON形式の構成ファイルまたはコード内の事前計算メタデータを提供する必要があります。
構成ファイルは手動で作成できますが、より便利な方法は、トレース・エージェント(これ以降、エージェントと呼びます)を使用して構成を生成することです。このガイドでは、エージェントを使用してnative-image
を構成する方法を示します。JVMでアプリケーションを実行すると、エージェントによって構成が自動的に生成されます。
コード内の事前計算されたメタデータを使用してネイティブ実行可能ファイルをビルドする方法を学習するには、このガイドに従ってください。
このガイドのサンプル・アプリケーションでは、Javaリフレクションを使用します。native-image
ツールでは、JavaリフレクションAPIを使用してアクセスされるアプリケーション要素が部分的にのみ検出されます。そのため、リフレクティブにアクセスされるクラス、メソッドおよびフィールドの詳細を提供する必要があります。
構成を指定しない例
次のアプリケーションでは、Javaリフレクションの使用方法を示しています。
- 次のソース・コードをReflectionExample.javaという名前のファイルに保存します:
import java.lang.reflect.Method; class StringReverser { static String reverse(String input) { return new StringBuilder(input).reverse().toString(); } } class StringCapitalizer { static String capitalize(String input) { return input.toUpperCase(); } } public class ReflectionExample { public static void main(String[] args) throws ReflectiveOperationException { if (args.length == 0) { System.err.println("You must provide the name of a class, the name of its method and input for the method"); return; } String className = args[0]; String methodName = args[1]; String input = args[2]; Class<?> clazz = Class.forName(className); Method method = clazz.getDeclaredMethod(methodName, String.class); Object result = method.invoke(null, input); System.out.println(result); } }
このJavaアプリケーションは、コマンドライン引数を使用して、実行する操作を決定します。
- 例をコンパイルし、次の各コマンドを実行します。
$JAVA_HOME/bin/javac ReflectionExample.java $JAVA_HOME/bin/java ReflectionExample StringReverser reverse "hello" $JAVA_HOME/bin/java ReflectionExample StringCapitalizer capitalize "hello"
各コマンドの出力は、それぞれ
"olleh"
および"HELLO"
になります。(クラスまたはメソッドを識別する他の文字列を指定すると、例外がスローされます。) - 次のように、
native-image
ユーティリティを使用して、ネイティブ実行可能ファイルを作成します:$JAVA_HOME/bin/native-image --no-fallback ReflectionExample
ノート:
--no-fallback
オプションをnative-image
にすると、実行可能ファイルを作成できない場合、ユーティリティは失敗します。 - 次のコマンドを使用して、生成されたネイティブ実行可能ファイルを実行します:
./reflectionexample StringReverser reverse "hello"
次のような例外が表示されます
Exception in thread "main" java.lang.ClassNotFoundException: StringReverser at java.lang.Class.forName(DynamicHub.java:1338) at java.lang.Class.forName(DynamicHub.java:1313) at ReflectionExample.main(ReflectionExample.java:25)
これは、静的分析から、
native-image
ツールがクラスStringReverser
がアプリケーションで使用されていると判断できなかったため、ネイティブ実行可能ファイルに含めなかったことを示しています。
構成を指定する例
次のステップでは、エージェントとその出力を使用して、リフレクションに依存し、構成を必要とするネイティブ実行可能ファイルを作成する方法を示します。
- 作業ディレクトリにMETA-INF/native-imageというディレクトリを作成します:
mkdir -p META-INF/native-image
- 次のように、エージェントを有効にしてアプリケーションを実行します:
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"
このコマンドは、クラス
StringReverser
の名前とそのreverse()
メソッドを含むreflection-config.jsonという名前のファイルを作成します。[ { "name":"StringReverser", "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }] } ]
- ネイティブ実行可能ファイルをビルドします:
$JAVA_HOME/bin/native-image ReflectionExample
native-image
ツールは、META-INF/native-imageディレクトリ内の構成ファイルを自動的に使用します。ただし、JARファイルまたは-cp
フラグを使用して、META-INF/native-imageディレクトリをクラスパスに配置することをお薦めします。(これにより、ディレクトリ構造がIDE自体によって定義されるIDEユーザーの混乱が回避されます。) - 実行可能ファイルをテストします。
./reflectionexample StringReverser reverse "hello" olleh ./reflectionexample StringCapitalizer capitalize "hello" Exception in thread "main" java.lang.ClassNotFoundException: StringCapitalizer at java.lang.Class.forName(DynamicHub.java:1338) at java.lang.Class.forName(DynamicHub.java:1313) at ReflectionExample.main(ReflectionExample.java:25)
トレース・エージェントも
native-image
ツールも、構成ファイルが完全であることを確認できません。エージェントは、プログラムの実行時にリフレクションを使用してアクセスされるプログラム要素を監視して記録します。この場合、native-image
ツールは、クラスStringCapitalizer
への参照を含めるように構成されていません。 - クラス
StringCapitalizer
を含めるように構成を更新します。次のように、reflection-config.jsonファイルを手動で編集するか、config-merge-dir
オプションを使用してトレース・エージェントを再実行して既存の構成ファイルを更新できます:$JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"
このコマンドは、クラス
StringCapitalizer
とそのcapitalize()
メソッドの名前を含めるようにreflection-config.jsonファイルを更新します。[ { "name":"StringCapitalizer", "methods":[{"name":"capitalize","parameterTypes":["java.lang.String"] }] }, { "name":"StringReverser", "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }] } ]
- ネイティブ実行可能ファイルを再ビルドして実行します。
$JAVA_HOME/bin/native-image ReflectionExample ./reflectionexample StringCapitalizer capitalize "hello"
これで、アプリケーションは意図したとおりに動作するはずです。