返回

使用追蹤代理程式配置原生映像檔

若要為使用 Java 反射、動態 Proxy 物件、JNI 或類別路徑資源的 Java 應用程式建置原生可執行檔,您應該提供 native-image 工具 JSON 格式的中繼資料檔案,或在程式碼中預先計算中繼資料。

您可以手動建立設定檔,但更方便的方法是使用追蹤代理程式 (以下簡稱代理程式) 產生組態。本指南示範如何使用代理程式配置 native-image。當您在 JVM 上執行應用程式時,代理程式會自動為您產生組態。

若要了解如何使用程式碼中預先計算的中繼資料建置原生可執行檔,請參閱文件

本指南中的範例應用程式使用 Java 反射。native-image 工具只會部分偵測使用 Java 反射 API 存取的應用程式元素。因此,您需要提供有關反射存取的類別、方法和欄位的詳細資訊。

無組態的範例

以下應用程式示範 Java 反射的使用。

先決條件

請確定您已安裝 GraalVM JDK。最簡單的入門方式是使用 SDKMAN!。如需其他安裝選項,請造訪下載區

  1. 將下列原始碼儲存為名為 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 應用程式使用命令列引數來判斷要執行的作業。

  2. 編譯範例,然後執行以下每個命令。
     javac ReflectionExample.java
    
     java ReflectionExample StringReverser reverse "hello"
    
     java ReflectionExample StringCapitalizer capitalize "hello"
    

    每個命令的輸出應分別為 "olleh""HELLO"。(如果您提供任何其他字串來識別類別或方法,則會擲回例外狀況。)

  3. 建立原生可執行檔,如下所示
     native-image --no-fallback ReflectionExample
    

    注意:native-image--no-fallback 選項會導致如果無法建立可執行檔,則公用程式會失敗。

  4. 使用以下命令執行產生的原生可執行檔
     ./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,因此未將其包含在原生可執行檔中。

具有組態的範例

以下步驟示範如何使用代理程式及其輸出,來建立依賴反射且需要組態的原生可執行檔。

  1. 在工作目錄中建立名為 META-INF/native-image/ 的目錄
     mkdir -p META-INF/native-image
    
  2. 使用啟用的代理程式執行應用程式,如下所示
     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"] }]
         }
       ]
     }
    
  3. 建置原生可執行檔
     native-image ReflectionExample
    

    native-image 工具會自動使用 META-INF/native-image/ 目錄中的中繼資料檔案。但是,我們建議 META-INF/native-image/ 目錄位於類別路徑上,透過 JAR 檔案或使用 -cp 選項。(這避免了 IDE 使用者在 IDE 本身定義目錄結構時產生混淆。)

  4. 測試您的可執行檔。
     ./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 的參照。

  5. 更新組態以包含類別 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"] }]
         }
       ]
    }
    
  6. 重新建置原生可執行檔並執行它。
     native-image ReflectionExample
    
     ./reflectionexample StringCapitalizer capitalize "hello"
    

    應用程式現在應該可以如預期般運作。

與我們聯繫