◀返回
使用追蹤代理程式配置原生映像檔
若要為使用 Java 反射、動態 Proxy 物件、JNI 或類別路徑資源的 Java 應用程式建置原生可執行檔,您應該提供 native-image
工具 JSON 格式的中繼資料檔案,或在程式碼中預先計算中繼資料。
您可以手動建立設定檔,但更方便的方法是使用追蹤代理程式 (以下簡稱代理程式) 產生組態。本指南示範如何使用代理程式配置 native-image
。當您在 JVM 上執行應用程式時,代理程式會自動為您產生組態。
若要了解如何使用程式碼中預先計算的中繼資料建置原生可執行檔,請參閱文件。
本指南中的範例應用程式使用 Java 反射。native-image
工具只會部分偵測使用 Java 反射 API 存取的應用程式元素。因此,您需要提供有關反射存取的類別、方法和欄位的詳細資訊。
無組態的範例
以下應用程式示範 Java 反射的使用。
先決條件
請確定您已安裝 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 --no-fallback ReflectionExample
注意:
native-image
的--no-fallback
選項會導致如果無法建立可執行檔,則公用程式會失敗。 - 使用以下命令執行產生的原生可執行檔
./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 -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"
此命令會建立名為 rechability-metadata.json 的檔案,其中包含類別
StringReverser
的名稱及其reverse()
方法。{ "reflection": [ { "type":"StringReverser", "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }] } ] }
- 建置原生可執行檔
native-image ReflectionExample
native-image
工具會自動使用 META-INF/native-image/ 目錄中的中繼資料檔案。但是,我們建議 META-INF/native-image/ 目錄位於類別路徑上,透過 JAR 檔案或使用-cp
選項。(這避免了 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
。您可以手動編輯 reachability-metadata.json 檔案,或重新執行追蹤代理程式,以使用config-merge-dir
選項更新現有的組態檔,如下所示java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"
此命令會更新 reachability-metadata.json 檔案,以包含類別
StringCapitalizer
的名稱及其capitalize()
方法。{ "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"
應用程式現在應該可以如預期般運作。