Quarkusの依存性注入(DI)とCDI基本アノテーションを初心者向けに徹底解説
生徒
「Quarkusって軽くて速いって聞いたんですが、DIとかCDIって何をするものなんですか?」
先生
「Quarkusでは、プログラムの部品同士を自動でつなげる仕組みとして、DIとCDIが使われています。自分でnewしなくても必要なクラスを使えるのが特徴です。」
生徒
「newを書かなくていいって、どういうことですか?」
先生
「Quarkusが代わりにオブジェクトを作って管理してくれるんです。基本アノテーションを理解すると、とても書きやすくなりますよ。」
1. QuarkusにおけるDIとCDIの考え方
Quarkusでは、依存性注入とCDIという仕組みを使って、クラス同士のつながりを自動的に管理します。依存性注入とは、あるクラスが必要とする別のクラスを、自分でnewして作るのではなく、外から渡してもらう考え方です。Quarkusではこの役割をフレームワークが担当するため、開発者は「何が必要か」だけを意識すればよくなります。
例えば、あいさつを返すクラスを別のクラスで使いたい場合でも、直接newを書く必要はありません。あらかじめ部品として登録しておくことで、自動的に利用できるようになります。これにより、コードの重複や管理ミスを防ぎ、初心者でも分かりやすい構造になります。
CDIはJava標準の仕組みで、Quarkusはこれを高速かつ軽量に利用できるよう最適化しています。難しく考えず、必要な部品を宣言するだけで使える仕組みと捉えると理解しやすいです。
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class MessageService {
public String message() {
return "Hello DI";
}
}
import jakarta.inject.Inject;
public class MessageController {
@Inject
MessageService service;
public String show() {
return service.message();
}
}
このように、MessageControllerの中でMessageServiceを自動的に使えるのがDIの基本です。部品同士のつながりをQuarkusに任せることで、シンプルで読みやすいコードを書くことができます。
2. @ApplicationScopedでアプリ全体に一つだけ作る
@ApplicationScopedは、アプリケーション全体で一つだけインスタンスを作り、それを使い回すためのアノテーションです。設定情報や共通処理をまとめたクラスでよく使われます。Quarkus起動時に一度だけ作られ、終了まで保持されます。
無駄なオブジェクト生成を防げるため、メモリ効率が良く、Quarkusの高速起動とも相性が良いのが特徴です。
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class GreetingService {
public String hello() {
return "Hello Quarkus";
}
}
3. @Injectで必要なクラスを自動的に使う
@Injectは、DIを行うための最も基本的なアノテーションです。これを付けることで、Quarkusが自動的に必要なクラスを探してセットしてくれます。自分でnewを書く必要がなくなり、コードがすっきりします。
初心者は、部品を自分で用意するのではなく、必要な場所で宣言するだけで使える点を意識すると理解しやすいです。
import jakarta.inject.Inject;
public class GreetingController {
@Inject
GreetingService service;
public String output() {
return service.hello();
}
}
4. @Singletonと@ApplicationScopedの違い
@Singletonも一つだけインスタンスを作る点では似ていますが、CDI管理という点で違いがあります。Quarkusでは基本的に@ApplicationScopedを使うことが推奨されます。CDIの機能をフルに活用でき、将来的な拡張にも対応しやすいからです。
初心者は、まず@ApplicationScopedを使うと覚えておくと混乱しにくくなります。
import jakarta.inject.Singleton;
@Singleton
public class TimeService {
public long now() {
return System.currentTimeMillis();
}
}
5. @RequestScopedでリクエストごとに管理する
@RequestScopedは、HTTPリクエストごとに新しいインスタンスを作るアノテーションです。Webアプリケーションでは、ユーザーごとやリクエスト単位で状態を分けたい場合があります。
QuarkusのREST APIと組み合わせると、リクエスト単位で安全にデータを扱えるようになります。
import jakarta.enterprise.context.RequestScoped;
@RequestScoped
public class RequestInfo {
public String info() {
return "request scope data";
}
}
6. コンストラクタインジェクションの基本
Quarkusでは、フィールドだけでなくコンストラクタを使った依存性注入も可能です。コンストラクタインジェクションは、必要な依存関係が明確になり、テストもしやすいというメリットがあります。
最初は少し難しく感じますが、クラス設計の理解が深まるため、慣れておくと役立ちます。
import jakarta.inject.Inject;
public class CalcService {
private final TimeService timeService;
@Inject
public CalcService(TimeService timeService) {
this.timeService = timeService;
}
public long calc() {
return timeService.now();
}
}
7. QuarkusでDIを使うメリット
Quarkusで依存性注入を使うと、コードの見通しが良くなり、修正や追加が簡単になります。部品ごとに役割が分かれるため、初心者でも全体構造を把握しやすくなります。
また、GraalVM対応や高速起動といったQuarkusの特徴とも相性が良く、実務でも使いやすい設計になります。
まとめ
Quarkusにおける依存性注入とCDIの基本を振り返ると、アプリケーション開発においてクラス同士の結びつきをどのように管理するかが重要なポイントであることが分かります。従来のようにnewキーワードを使ってインスタンスを生成する方法では、クラス同士が強く結びついてしまい、変更や拡張が難しくなる傾向があります。しかし、依存性注入を活用することで、必要なオブジェクトは外部から提供されるため、柔軟で保守性の高い設計が実現できます。 特にQuarkusでは、CDIというJava標準の仕組みをベースにしながらも、高速起動や軽量動作に最適化されている点が大きな特徴です。ApplicationScopedやRequestScopedといったスコープを適切に使い分けることで、アプリケーション全体のパフォーマンスやメモリ効率を意識した設計が可能になります。例えば、共通処理や設定値を扱うクラスにはApplicationScopedを使用し、リクエスト単位でデータを扱う場合にはRequestScopedを利用することで、安全かつ効率的な処理が実現できます。 また、Injectアノテーションを利用することで、必要な依存関係を明示的に記述するだけで自動的にオブジェクトが提供される仕組みは、コードの可読性を大きく向上させます。さらに、コンストラクタインジェクションを活用することで、依存関係が明確になり、テストコードの作成や将来的な機能追加にも強い構造を作ることができます。このように、依存性注入は単なる便利機能ではなく、設計の質を高めるための重要な考え方であるといえます。 初心者のうちは、ApplicationScopedとInjectの組み合わせから理解を始めるのがおすすめです。これにより、基本的なDIの流れを体験しながら、徐々にスコープや設計の違いを理解していくことができます。Quarkusは学習コストが比較的低く、実務にも直結しやすいフレームワークであるため、DIとCDIの理解は今後のJava開発において大きな武器になります。 以下に、これまでの内容を整理したシンプルなサンプルプログラムを掲載します。DIとCDIの基本的な流れを確認するための例として参考にしてください。
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class MessageService {
public String getMessage() {
return "Quarkus DI and CDI sample";
}
}
public class MessageController {
@Inject
MessageService messageService;
public void execute() {
System.out.println(messageService.getMessage());
}
public static void main(String[] args) {
MessageController controller = new MessageController();
controller.execute();
}
}
Quarkus DI and CDI sample
生徒
「依存性注入って、最初は難しそうだと思っていたんですが、実際にはクラス同士のつながりをシンプルにする仕組みなんですね。」
先生
「その通りです。自分でオブジェクトを作るのではなく、必要なものを外から渡してもらうことで、コードの柔軟性が高まります。」
生徒
「ApplicationScopedやRequestScopedの違いも、使いどころが分かると便利ですね。」
先生
「はい。スコープを意識することで、無駄なインスタンス生成を防ぎつつ、安全にデータを扱うことができます。」
生徒
「Injectを使うだけでクラスが使えるのは驚きでした。newを書かないのに動くのが不思議です。」
先生
「そこがDIの強みです。フレームワークが裏で管理してくれるので、開発者はロジックに集中できます。」
生徒
「コンストラクタインジェクションも覚えておくと、設計がより分かりやすくなりそうですね。」
先生
「その理解で大丈夫です。依存関係を明確にすることは、良い設計につながります。今回の内容をしっかり押さえておけば、実務でも役立ちますよ。」