トレース・エージェントを使用したネイティブ・イメージの構成
Javaリフレクション、動的プロキシ・オブジェクト、JNIまたはクラス・パス・リソースを使用するJavaアプリケーションのネイティブ実行可能ファイルをビルドするには、native-image
ツールにJSON形式のメタデータ・ファイルまたはコード内の事前計算メタデータを提供する必要があります。
構成ファイルは手動で作成できますが、より便利な方法は、トレース・エージェント(これ以降、エージェントと呼びます)を使用して構成を生成することです。このガイドでは、エージェントを使用してnative-image
を構成する方法を示します。JVMでアプリケーションを実行すると、エージェントによって構成が自動的に生成されます。
コード内の事前計算されたメタデータを使用してネイティブ実行可能ファイルをビルドする方法については、ドキュメントを参照してください。
このガイドのサンプル・アプリケーションでは、リフレクションを使用します。native-image
ツールは、JavaリフレクションAPIを通じてアクセスされるアプリケーション要素を部分的にのみ検出できます。したがって、リフレクティブにアクセスされるクラス、メソッドおよびフィールドの詳細を明示的に指定する必要があります。
構成を指定しない例
前提条件
GraalVM JDKがインストール済であることを確認します。最も簡単に始めるには、SDKMAN!を使用します。その他のインストール・オプションについては、「ダウンロード」セクションを参照してください。
- 次のソース・コードを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アプリケーションは、コマンドライン引数を使用して、実行する操作を決定します。
- 例をコンパイルし、次の各コマンドを実行します。
javac ReflectionExample.java
java ReflectionExample StringReverser reverse "hello"
java ReflectionExample StringCapitalizer capitalize "hello"
各コマンドの出力は、それぞれ
"olleh"
および"HELLO"
になります。(クラスまたはメソッドを識別する他の文字列を指定すると、例外がスローされます。) - 次のように、ネイティブ実行可能ファイルを作成します:
native-image ReflectionExample
- 次のコマンドを使用して、生成されたネイティブ実行可能ファイルを実行します:
./reflectionexample StringReverser reverse "hello"
次のような例外が表示されます:
Exception in thread "main" java.lang.ClassNotFoundException: StringReverser at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:190) ... at ReflectionExample.main(ReflectionExample.java:68)
これは、静的分析から、
native-image
ツールがクラスStringReverser
がアプリケーションで使用されていると判断できなかったため、ネイティブ実行可能ファイルに含めなかったことを示しています。
構成を指定する例
次のステップでは、エージェントとその出力を使用して、リフレクションに依存し、構成を必要とするネイティブ実行可能ファイルを作成する方法を示します。
- 作業ディレクトリにMETA-INF/native-image/というディレクトリを作成します:
mkdir -p META-INF/native-image
- 次のように、エージェントを有効にしてアプリケーションを実行します:
java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"
このコマンドは、クラス
StringReverser
の名前とそのreverse()
メソッドを含むrechability-metadata.jsonという名前のファイルを作成します。{ "reflection": [ { "type": "StringReverser", "methods": [ { "name": "reverse", "parameterTypes": [ "java.lang.String" ] } ] } ] }
- ネイティブ実行可能ファイルをビルドします:
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 org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:190) ... at ReflectionExample.main(ReflectionExample.java:68)
トレース・エージェントも
native-image
ツールも、構成ファイルが完全であることを確認できません。エージェントは、アプリケーションの実行時にリフレクションを使用してアクセスされるプログラム要素を監視して記録します。この場合、native-image
ツールは、クラスStringCapitalizer
への参照を含めるように構成されていません。 - クラス
StringCapitalizer
を含めるように構成を更新します。次のように、reachability-metadata.jsonファイルを手動で編集するか、config-merge-dir
オプションを使用してトレース・エージェントを再実行して既存の構成ファイルを更新できます:java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"
このコマンドは、クラス
StringCapitalizer
とそのcapitalize()
メソッドの名前を含めるようにreachability-metadata.jsonファイルを更新します。{ "reflection": [ { "type": "StringCapitalizer", "methods": [ { "name": "capitalize", "parameterTypes": [ "java.lang.String" ] } ] }, { "type": "StringReverser", "methods": [ { "name": "reverse", "parameterTypes": [ "java.lang.String" ] } ] } ] }
- ネイティブ実行可能ファイルを再ビルドして実行します。
native-image ReflectionExample
./reflectionexample StringCapitalizer capitalize "hello"
これで、アプリケーションは意図したとおりに動作するはずです。