以 Quarkus 原生鏡像存取資源
1. 引言
Quarkus 是一個 Java 框架,它承諾提供體積小巧、啟動速度極快以及首次請求時間更短的解決方案。我們透過使用 GraalVM 建立原生鏡像來實現這些目標。在開發 Quarkus 應用程式時,我們可能需要存取一些資源,例如鏡像中包含的或外部提供的文字檔案。
在本教程中,我們將探討如何以不同的方式向打包為 Quarkus Native Image 的應用程式提供資源。
2. 專案配置
我們將建立一個範例項目,以幫助我們了解在 Quarkus Native Image 中載入資源檔案的不同方法。
2.1 建立項目
我們需要預先安裝 JDK 和 Quarkus CLI。安裝完成後,即可建立新專案:
$ quarkus create app quarkus-resources
這應該會在名為quarkus-resources資料夾下建立一個新項目,其中包含 Quarkus 項目的基本框架。
2.2. 依賴關係
使用 Quarkus CLI 建立的專案會自動新增核心相依性( [quarkus-arc](https://mvnrepository.com/artifact/io.quarkus/quarkus-arc)和[quarkus-junit5](https://mvnrepository.com/artifact/io.quarkus/quarkus-junit5) )以及 REST 服務相依性( [quarkus-rest](https://mvnrepository.com/artifact/io.quarkus/quarkus-rest) , [rest-assured](https://mvnrepository.com/artifact/io.rest-assured/rest-assured) )。此外,我們還需要quarkus-rest-jackson來處理 REST 服務輸入或輸出中的 JSON 資料:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
3. 獲取資源
現在專案框架已經搭建完畢,我們可以開始透過程式碼存取資源了。
需要注意的是,GraalVM 不會將類別路徑上的任何資源包含在它所建立的本機執行檔中。**需要包含在本機可執行檔中的資源需要明確配置。**
Quarkus 為我們提供了幾種不同的方式來存取應用程式內部的資源。
3.1 公共資源
與任何 Web 應用程式一樣,我們通常需要公開一些靜態 Web 內容,例如 HTML、JavaScript 和 CSS。 Quarkus 會自動將META-INF/resources目錄中的內容作為 Web 內容公開。
我們注意到META-INF/resources目錄僅用於存放公共資源。不應在此目錄中放置任何非公共資源。該目錄可用於放置各種類型的網頁內容,但為了方便測試,我們在此目錄中放置了一個簡單的index.html ,其內容如下:
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello from Baeldung!</h1>
</body>
</html>
然後,我們新增一個測試來檢查內容:
@Test
@DisplayName("should return content from index.html")
void givenIndexPage_whenGetRootUrl_thenReturnsContent() {
given()
.when().get("/")
.then()
.statusCode(200)
.body(containsString("Hello"));
}
3.2 屬性配置
我們將建立兩個名為default-resource.txt和text/another-resource.txt的文字文件,並將它們放在資源目錄src/main/resources.與jar打包不同, src/main/resources的內容不會自動包含在原生鏡像中。
因此,我們注意到,這些資源需要明確配置才能在程式碼中使用。此配置可以以逗號分隔的 glob 模式清單的形式新增至application.properties :
quarkus.native.resources.includes=text/**,*.txt
然後,我們將建立一個方法,允許我們存取給定的資源:
private String readResource(String resourcePath) throws IOException {
LOGGER.info("Reading resource from path: {}", resourcePath);
try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath)) {
if (in == null) {
LOGGER.error("Resource not found at path: {}", resourcePath);
throw new IOException("Resource not found: " + resourcePath);
}
LOGGER.info("Successfully read resource: {}", resourcePath);
return new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
}
}
最後,我們將新增一個方法,透過 API 端點公開這些資源的內容:
@GET
@Path("/default”)
@Produces(MediaType.TEXT_PLAIN)
public Response getDefaultResource() throws IOException {
return Response.ok(readResource("default-resource.txt")).build();
}
至此,我們已成功從類別路徑中讀取了default-resource.txt文件,並透過 API 公開了其內容。在實際專案中,我們可能會以不同的方式使用這些內容。接下來,我們將使用 JUnit 進行測試:
@Test
@DisplayName("should return content from default resource")
void whenGetDefaultResource_thenReturnsContent() {
given()
.when().get("/resources/default")
.then()
.statusCode(200)
.body(is("This is the default resource."));
}
3.3 資源配置文件
雖然application.properties配置提供了一種快速存取資源的方法,但有時配置會更加複雜。在這種情況下,我們可能需要提供多種不同的模式來涵蓋感興趣的文件。
GraalVM 允許透過使用resource-config.json進行此類配置:
{
"resources": [
{
"pattern": ".*\\.json$"
}
]
}
我們需要將此檔案放置在src/main/resources/META-INF/native-image/<group-id>/<artifact-id>,然後 Quarkus 建置過程會自動將這些檔案提供給程式碼使用。接下來,我們建立一個單獨的端點來公開此文件的內容:
@GET
@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
public Response getJsonResource() throws IOException {
return Response.ok(readResource("resources.json")).build();
}
現在我們可以測試一下:
@Test
@DisplayName("should return content from included json resource")
void whenGetJsonResource_thenReturnsContent() {
given()
.when().get("/resources/json")
.then()
.statusCode(200)
.body("version", is("1.0.0"));
}
現在我們已經成功地使用資源設定檔和模式公開了資源。
4. API 的手動測試
我們現在有一些 API 暴露了一些不同的資源,並且編寫了單元測試來檢查其他 API 呼叫的預期輸出。但是,我們注意到 JUnit 測試不會測試原生鏡像,因此,要真正測試資源的可用性,需要測試原生建置中相關的 API URL。
因此,為了正確測試這一點,我們可以運行專案的原生建置版本:
$ mvn clean install -Pnative
這將在target問題下建立一個格式為<project-name>-<version>-runner的本機可執行檔。我們可以運行此可執行文件,然後使用瀏覽器或curl命令對其進行測試:
$ curl -X GET http://localhost:8080/resources/default
我們預期此 URL 將傳回default-resource.txt檔案的內容。同樣,我們也可以手動測試其他 URL。
5. 結論
本文中,我們使用 Quarkus 配置了一個 API,並使用 Quarkus 原生鏡像存取了伺服器上的檔案資源。我們回顧了從程式碼存取類別路徑資源的不同方法。存取類別路徑上的資源需要明確配置。
和往常一樣,程式碼可以在 GitHub 上找到。