Java のクラウドネイティブ化が当たり前になり、軽量・高速なランタイムとして Quarkus を採用するケースが増えてきています。 一方で、既存アプリケーションの多くは Spring Boot で構築されており、
- 本当に Quarkus へ移行できるのか
- コードを書き直さないといけないのでは
といった印象を持たれがちです。
しかし実は、REST API アプリケーションであれば驚くほど簡単に動作します。 理由は、Quarkus が Spring Boot 互換 API を公式に提供しているためです。
今回は、次の公開サンプル(著者許諾済み)を題材に、Spring Boot の REST API を Quarkus へ移行する方法と、実際の性能差を紹介します。
https://github.com/projava17/examples/tree/main/part-6/chapter-21-spring-boot
結論:REST API アプリは“ほぼ変更なし”で Quarkus 上で動く
今回の移行検証では、REST API 部分は メソッドに public を追加するだけでそのまま Quarkus 上で動作しました。
Quarkus 側では Spring Web 互換レイヤー(quarkus-spring-web)が提供されているため、
@RestController@RequestMapping@GetMapping/@PostMapping
といったアノテーションはほぼ Spring Boot と同じように記述できます。
BFF のような薄い REST API アプリなら、コードを書き換える量は最小限で済みます。
性能はどう変わったのか(実測値)
実際に計測すると、Quarkus へ移行するだけで次の改善が見られました。
| 項目 | Spring Boot | Quarkus | 差分 |
|---|---|---|---|
| 起動時間 | 1.2 秒 | 0.58 秒 | 約 52% 短縮 |
| 起動後のメモリ使用量 | 12 MB | 8 MB | 約 33% 削減 |
つまり 書き換えほぼゼロで高速化が得られるということです。 これはクラウド環境ではコスト削減にも直結します。 メモリ使用量は、GCを何度か実行した後の値を取得しています。
移行の全体像
今回の移行ステップはとてもシンプルです。
- pom.xml を Spring Boot から Quarkus の BOM に書き換える
- Spring Boot 固有の起動クラス(
TaskListApplication)を削除 - REST API のメソッドに
publicを付ける(Spring Boot は package-private でも動くが、Quarkus は public 必須) - Thymeleaf を Qute に置き換える(今回は“おまけ”として後述)
まずは最も重要な pom.xml から見ていきます。
Spring Boot → Quarkus への pom.xml の変更ポイント
元の Spring Boot(抜粋)
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.3</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Quarkus 用に書き換えた pom.xml(BOM + Spring Web 互換 API)
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-spring-web</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest-jackson</artifactId> </dependency>
ここが最大のポイントです。 Spring Boot の Web API を提供する部分を Quarkus の Spring Web 互換レイヤーに差し替えるのみで、実際のコントローラ実装はそのまま利用できます。
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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jp.gihyo.projava</groupId> <artifactId>tasklist</artifactId> <version>0.0.1-SNAPSHOT</version> <name>TaskList</name> <description>Spring Bootを使ったタスク管理アプリケーション</description> <properties> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version>3.30.2</quarkus.platform.version> <surefire-plugin.version>3.1.2</surefire-plugin.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>${quarkus.platform.artifact-id}</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-spring-web</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkiverse.qute.web</groupId> <artifactId>quarkus-qute-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>${surefire-plugin.version}</version> <configuration> <systemPropertyVariables> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> </systemPropertyVariables> </configuration> </plugin> <plugin> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>quarkus-maven-plugin</artifactId> <version>${quarkus.platform.version}</version> <executions> <execution> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
アプリケーション起動クラスは不要
Spring Boot の典型的な起動クラスは以下のような構造です。
@SpringBootApplication public class TaskListApplication { public static void main(String[] args) { SpringApplication.run(TaskListApplication.class, args); } }
Quarkus は ランタイムが起動処理を持つため、アプリ側で明示的に記述する必要がありません。 そのため、このクラスは削除します。
REST API の修正点:本当に “public を付けるだけ”
実際に変更したコードがこちらです。
Before(Spring Boot)
@RequestMapping(value = "/resthello") String hello() { return """ Hello. It works! 現在時刻は%sです。 """.formatted(LocalDateTime.now()); }
After(Quarkus)
@RequestMapping(value = "/resthello") public String hello() { return """ Hello. It works! 現在時刻は%sです。 """.formatted(LocalDateTime.now()); }
本当にこれだけです。 Spring Boot がメソッド可視性に寛容なのに対して、Quarkus は CDI の仕様上 public を要求するため、この差だけが必要となります。
まとめ:既存 Spring Boot REST API は “そのまま高速なランタイムで動かせる”
今回の検証で分かったのは次のポイントです。
- REST API アプリなら ほぼ変更なしで Quarkus に移行可能
- メソッドに public を付ける程度で動作
- 互換 API により Spring Boot の資産を維持できる
- 起動時間 50% 削減、メモリ 30% 削減と効果が大きい
- BFF のような薄い API 層との相性が特に良い
“コードは Spring のまま、ランタイムだけ Quarkus で高速化” というアプローチは、既存プロジェクトの延命・最適化において非常に有効です。
おまけ:画面(HTML テンプレート)の移行
今回のアプリケーションには Thymeleaf の画面が含まれていますが、テンプレートエンジンの移行は別問題です。
- Quarkus は
@Controllerを提供していない - Quarkus ではテンプレートエンジンとして Qute を使用する
- Thymeleaf の名前空間や式はそのままでは使えない





