◀返回
使用 Native Image Maven 外掛程式加入可達性元資料
您可以使用 Maven 從 Java 應用程式建置原生可執行檔。為此,請使用作為 Native Build Tools 專案一部分提供的 GraalVM Native Image Maven 外掛程式。
一個「真實世界」的 Java 應用程式可能需要一些 Java 反射物件,或者它會呼叫一些原生程式碼,或者存取類別路徑上的資源 - native-image
工具在建置時必須知道的動態功能,並以 元資料 的形式提供。(Native Image 在建置時動態載入類別,而不是在執行時載入。)
根據您的應用程式相依性,有三種方法可以提供元資料
- 使用 GraalVM 可達性元資料儲存庫
- 使用追蹤代理程式
- 自動偵測(如果所需的資源直接位於類別路徑上的 src/main/resources/ 目錄中)
本指南示範如何使用 GraalVM 可達性元資料儲存庫和 追蹤代理程式來建置原生可執行檔。本指南的目標是說明這兩種方法的差異,並示範如何使用可達性元資料可以簡化您的開發任務。
我們建議您按照說明逐步建立應用程式。或者,您可以直接前往完成的範例。
準備示範應用程式
先決條件
請確定您已安裝 GraalVM JDK。最簡單的開始方式是使用 SDKMAN!。如需其他安裝選項,請瀏覽下載區段。
-
在您最愛的 IDE 中或從命令列使用 Maven 建立一個名為「H2Example」的新 Java 專案,在
org.graalvm.example
套件中。 - 開啟主類別檔案,src/main/java/org/graalvm/example/H2Example.java,並將其內容替換為以下內容
package org.graalvm.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; public class H2Example { public static final String JDBC_CONNECTION_URL = "jdbc:h2:./data/test"; public static void main(String[] args) throws Exception { // Cleanup withConnection(JDBC_CONNECTION_URL, connection -> { connection.prepareStatement("DROP TABLE IF EXISTS customers").execute(); connection.commit(); }); Set<String> customers = Set.of("Lord Archimonde", "Arthur", "Gilbert", "Grug"); System.out.println("=== Inserting the following customers in the database: "); printCustomers(customers); // Insert data withConnection(JDBC_CONNECTION_URL, connection -> { connection.prepareStatement("CREATE TABLE customers(id INTEGER AUTO_INCREMENT, name VARCHAR)").execute(); PreparedStatement statement = connection.prepareStatement("INSERT INTO customers(name) VALUES (?)"); for (String customer : customers) { statement.setString(1, customer); statement.executeUpdate(); } connection.commit(); }); System.out.println(""); System.out.println("=== Reading customers from the database."); System.out.println(""); Set<String> savedCustomers = new HashSet<>(); // Read data withConnection(JDBC_CONNECTION_URL, connection -> { try (ResultSet resultSet = connection.prepareStatement("SELECT * FROM customers").executeQuery()) { while (resultSet.next()) { savedCustomers.add(resultSet.getObject(2, String.class)); } } }); System.out.println("=== Customers in the database: "); printCustomers(savedCustomers); } private static void printCustomers(Set<String> customers) { List<String> customerList = new ArrayList<>(customers); customerList.sort(Comparator.naturalOrder()); int i = 0; for (String customer : customerList) { System.out.println((i + 1) + ". " + customer); i++; } } private static void withConnection(String url, ConnectionCallback callback) throws SQLException { try (Connection connection = DriverManager.getConnection(url)) { connection.setAutoCommit(false); callback.run(connection); } } private interface ConnectionCallback { void run(Connection connection) throws SQLException; } }
-
刪除 H2Example/src/test/java/ 目錄(如果存在)。
- 開啟專案組態檔案,pom.xml,並將其內容替換為以下內容
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.graalvm.buildtools.examples</groupId> <artifactId>maven</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <h2.version>2.2.220</h2.version> <!-- Replace with your Java version --> <java.version>22</java.version> <imageName>h2example</imageName> <mainClass>org.graalvm.example.H2Example</mainClass> </properties> <dependencies> <!-- 1. H2 Database dependency --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${h2.version}</version> </dependency> </dependencies> <!-- 2. Native Image Maven plugin within a Maven profile --> <profiles> <profile> <id>native</id> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <version>0.10.1</version> <extensions>true</extensions> <executions> <execution> <id>build-native</id> <goals> <goal>compile-no-fork</goal> </goals> <phase>package</phase> </execution> </executions> <configuration> <buildArgs> <!-- 3. Quick build mode --> <buildArg>-Ob</buildArg> </buildArgs> </configuration> </plugin> </plugins> </build> </profile> </profiles> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M5</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>${java.version}</source> <target>22</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>${mainClass}</mainClass> </manifest> </archive> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <id>java</id> <goals> <goal>java</goal> </goals> <configuration> <mainClass>${mainClass}</mainClass> </configuration> </execution> <execution> <id>native</id> <goals> <goal>exec</goal> </goals> <configuration> <executable>${project.build.directory}/${imageName}</executable> <workingDirectory>${project.build.directory}</workingDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
1 加入 H2 資料庫的相依性,這是一個適用於 Java 的開放原始碼 SQL 資料庫。應用程式透過 JDBC 驅動程式與此資料庫互動。
2 在附加到
package
階段的 Maven 設定檔中啟用 Native Image Maven 外掛程式。(您將使用 Maven 設定檔建置原生可執行檔。)Maven 設定檔可讓您決定是要只建置 JAR 檔案,還是要建置原生可執行檔。外掛程式會探索它需要傳遞給native-image
的 JAR 檔案,以及可執行檔的主類別應該是什麼。3 您可以使用
<buildArgs>
區段將參數傳遞給底層的native-image
建置工具。在個別的<buildArg>
標籤中,您可以傳遞參數的方式與從命令列完全相同。-Ob
選項可啟用快速建置模式(建議僅在開發期間使用),作為範例。從外掛程式的文件中瞭解其他組態選項。 - (選用)建置應用程式。從儲存庫的根目錄執行下列命令
mvn clean package
這會產生一個「可執行」的 JAR 檔案,其中包含應用程式的所有相依性,以及正確設定的 MANIFEST 檔案。
使用 GraalVM 可達性元資料儲存庫建置原生可執行檔
Native Image Maven 外掛程式支援 GraalVM 可達性元資料儲存庫。此儲存庫提供預設不支援 GraalVM Native Image 的程式庫的 GraalVM 組態。其中之一是此應用程式所依賴的 H2 資料庫。需要明確啟用支援。
- 開啟 pom.xml,並將下列內容加入
native
設定檔的<configuration>
元素中,以啟用 GraalVM 可達性元資料儲存庫<metadataRepository> <enabled>true</enabled> </metadataRepository>
組態區塊應如下所示
<configuration> <buildArgs> <buildArg>-Ob</buildArg> </buildArgs> <metadataRepository> <enabled>true</enabled> </metadataRepository> </configuration>
外掛程式會自動從儲存庫下載元資料。
- 現在使用設定檔建置原生可執行檔(請注意,設定檔名稱是以
-P
旗標指定)mvn package -Pnative
這會在 target/ 目錄中產生一個用於平台的原生可執行檔,名為
h2example
。 -
從原生可執行檔執行應用程式
./target/h2example
應用程式會傳回儲存在 H2 資料庫中的客戶清單。
使用追蹤代理程式建置原生可執行檔
提供 native-image
的元資料組態的第二種方法是在編譯時注入追蹤代理程式(稍後稱為「代理程式」)。預設會停用代理程式,但可以在您的 pom.xml 檔案中或透過命令列啟用。
代理程式可以在三種模式下執行
- 標準:收集沒有條件的元資料。如果您要建置原生可執行檔,建議使用此選項。(預設)
- 條件式:收集具有條件的元資料。如果您要為打算進一步使用的原生共用程式庫建立條件式元資料,建議使用此選項。
- 直接:僅適用於進階使用者。此模式允許直接控制傳遞至代理程式的命令列。
請參閱以下如何使用追蹤代理程式收集元資料,以及建置套用所提供組態的原生可執行檔。
- 在
native
設定檔的<configuration>
元素中加入下列內容來啟用代理程式<agent> <enabled>true</enabled> </agent>
組態區塊應如下所示
<configuration> <agent> <enabled>true</enabled> </agent> <buildArgs> <buildArg>-Ob</buildArg> </buildArgs> <metadataRepository> <enabled>true</enabled> </metadataRepository> </configuration>
- 使用已啟用的代理程式執行應用程式會比較複雜,而且需要您設定個別的 MOJO 執行,以允許分支 Java 程序。在
native
Maven 設定檔區段中,加入exec-maven-plugin
外掛程式<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <id>java-agent</id> <goals> <goal>exec</goal> </goals> <phase>test</phase> <configuration> <executable>java</executable> <workingDirectory>${project.build.directory}</workingDirectory> <arguments> <argument>-classpath</argument> <classpath/> <argument>${mainClass}</argument> </arguments> </configuration> </execution> </executions> </plugin>
- 在 JVM 上使用已啟用的代理程式執行您的應用程式
mvn -Pnative -Dagent=true -DskipTests -DskipNativeBuild=true package exec:exec@java-agent
代理程式會擷取並記錄對 H2 資料庫的呼叫,以及在測試執行期間遇到的所有動態功能,並將其記錄到 target/native/agent-output/main/ 目錄中的多個 *-config.json 檔案中。
- 使用代理程式收集的組態建置原生可執行檔
mvn -Pnative -Dagent=true -DskipTests package exec:exec@native
它會在 target/ 目錄中產生一個用於平台的原生可執行檔,名為 h2example。
- 從原生可執行檔執行應用程式
./target/h2example
- (選用)若要清除專案,請執行
mvn clean
,並刪除目錄 META-INF/ 及其內容。
摘要
本指南示範如何使用 GraalVM 可達性元資料儲存庫和追蹤代理程式建置原生可執行檔。目標是顯示差異,並證明使用可達性元資料如何簡化工作。
請注意,如果您的應用程式在執行時沒有呼叫任何動態功能,則啟用 GraalVM 可達性元資料儲存庫是不必要的。在這種情況下,您的工作流程將會是
mvn package -Pnative