Java | Spring Boot | 依存性の注入 | DIコンテナ
Java
Spring Bootの"DIコンテナ"
- "DIコンテナ"を理解するには、依存性の注入(DI:Dependency Injection)についての理解が必要。Spring Bootでの依存性の注入(DI:Dependency Injection)は、"DIコンテナ"が自動的に注入している。
- Spring Bootでは、クラスを「コンポーネント」として扱い、これらのコンポーネントが互いに依存する場合に、DIコンテナがその依存関係を自動的に注入している。これにより、手動でインスタンスを作成する必要がなくなり、コードがクリーンで保守しやすくなる。
項目 | 内容 |
---|---|
DIコンテナ | Spring Bootは”DIコンテナ”を提供し、オブジェクトのライフサイクルを管理し、依存関係を自動的に解決する。 |
コンポーネント | @Component アノテーションを使って、クラスをSpringのコンポーネントとして登録する。 |
注入 | @Autowired アノテーションを使って、依存関係を注入する。 |
柔軟性 | DIを利用することで、異なる実装を簡単に切り替えられる柔軟な設計が可能になる。 |
- Spring Bootの"DIコンテナ"を利用することで、依存性の管理が非常に効率的になり、テストやメンテナンスが容易になる。
- Spring Bootは、"DIコンテナ"と呼ばれる基盤でオブジェクトのインスタンスを管理している。
- この"DIコンテナ"で管理されるコンポーネントを"Bean"と呼ぶ。
Spring BootでのDIの実装
以下のノーマルコードをSpring Bootフレームワークで実装してみる。
/* Spring Bootフレームワークを使わないで実装したコード */
/* C の別の実装(異なる処理を行うことを想定)を作成し、B に注入する例。 */
/* C の別の実装として CAlternative クラスを作成している。 */
class C {
public void doSomething() {
System.out.println("C is doing something.");
}
}
/* C の別の実装(異なる処理を行うことを想定)として CAlternative クラスを作成 */
class CAlternative {
public void doSomething() {
System.out.println("CAlternative is doing something different.");
}
}
class B {
private C c;
// Cをコンストラクタで注入
public B(C c) {
this.c = c;
}
public void doSomething() {
c.doSomething();
}
}
class A {
private B b;
// Bをコンストラクタで注入
public A(B b) {
this.b = b;
}
public void start() {
b.doSomething();
}
}
// 使用例
public class Main {
public static void main(String[] args) {
// Cのインスタンスを作成してBに注入
C c = new C();
B b = new B(c);
A a = new A(b);
a.start(); // "C is doing something." と表示される
// CAlternativeのインスタンスを作成してBに注入
CAlternative cAlternative = new CAlternative();
B bAlternative = new B(cAlternative);
A aAlternative = new A(bAlternative);
aAlternative.start(); // "CAlternative is doing something different." と表示される
}
}
1. コンポーネントの定義
まず、依存関係となるクラス C とその代替実装 CAlternative を作成し、@Component アノテーションを使ってSpringのコンポーネントとして定義する。
import org.springframework.stereotype.Component;
@Component
class C {
public void doSomething() {
System.out.println("C is doing something.");
}
}
@Component
class CAlternative {
public void doSomething() {
System.out.println("CAlternative is doing something different.");
}
}
2. サービスクラスの定義
次に、依存性を注入される B クラスを作成する。
ここでも @Component アノテーションを使う。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
class B {
private final C c;
@Autowired // Cのインスタンスを自動的に注入
public B(C c) {
this.c = c;
}
public void doSomething() {
c.doSomething();
}
}
- @Autowiredアノテーション は、Spring の IoC(Inversion of Control)コンテナが、必要な依存オブジェクトを自動的に注入することを指示するために使用する。これにより、クラスが必要とする他のクラスのインスタンスを手動で作成する必要がなくなる。
- @Autowiredアノテーション によって、Spring Boot は B のインスタンスを作成する際に、必要な C のインスタンスを自動的に提供する。開発者は C を自分で新しく作成したり、管理したりする必要がなく、依存性が自動的に解決されるため、コードが簡潔になり、可読性が向上する。
- B のインスタンスを作成する際に、@Autowired により C のインスタンスが自動的に注入されるため、B の doSomething() メソッドを呼び出すと、C のメソッドを利用できる状態となる。
3. コントローラクラスの定義
最後に、A クラスを作成し、B を注入する。
ここでも @Component アノテーションを使用する。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
class A {
private final B b;
@Autowired // Bのインスタンスを自動的に注入
public A(B b) {
this.b = b;
}
public void start() {
b.doSomething();
}
}
- @Autowiredアノテーション は、Spring の IoC(Inversion of Control)コンテナが、必要な依存オブジェクトを自動的に注入することを指示するために使用する。これにより、クラスが必要とする他のクラスのインスタンスを手動で作成する必要がなくなる。
- @Autowiredアノテーション によって、Spring Boot は A のインスタンスを作成する際に、必要な B のインスタンスを自動的に提供する。開発者は B を自分で新しく作成したり、管理したりする必要がなく、依存性が自動的に解決されるため、コードが簡潔になり、可読性が向上する。
- A のインスタンスを作成する際に、@Autowired により B のインスタンスが自動的に注入されるため、A の doSomething() メソッドを呼び出すと、B のメソッドを利用できる状態となる。
4. Spring Boot アプリケーションの設定
次に、Spring Boot アプリケーションを設定し、すべてのコンポーネントをスキャンして依存関係を注入する。
@SpringBootApplicationアノテーション でSpring Boot アプリケーションを設定し、すべてのコンポーネントをスキャンしている。
@SpringBootApplicationアノテーション が付与されたクラス内で @Beanアノテーション を使うことで、Bean 定義を行い、依存性注入を受けることができる。
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
@Bean
CommandLineRunner run(A a) {
return args -> {
a.start(); // Aのメソッドを呼び出す
};
}
}
MyApplication クラス は@SpringBootApplication でアノテートされているが、これは @Configuration, @EnableAutoConfiguration, @ComponentScan の3つでクラスをアノテートしたのと同じ扱いになる。
アノテーション | 内容 |
---|---|
@Configuration | Spring Bootの設定クラスであることを示す。Spring Bootの色々な設定を Java コード上で行えるようにするためのアノテーション。このクラス内でBeanの定義を行うことができる。昔の Spring は XML で設定が記述されていたが、今は Java コード上で設定を行うのが主流になっている。 |
@EnableAutoConfiguration | Spring Bootの設定を自動化するためのアノテーション。Spring Bootの自動設定機能が有効になる。これにより、アプリケーションに必要なBeanを自動的に設定させる。これがあることで、依存関係を追加するだけで Spring MVC などのライブラリを設定記述なしで使えるようになる。 |
@ComponentScan | "DIコンテナ"が管理する Bean を自動登録するためのアノテーション。指定されたパッケージ(デフォルトではアプリケーションクラスのパッケージ)をスキャンし、コンポーネントを検出する。 |
@Bean
CommandLineRunner run(A a) {
return args -> {
a.start(); // Aのメソッドを呼び出す
};
}
run(A a) メソッドに @Bean アノテーションが付与されている。このメソッドが CommandLineRunner のインスタンスを生成し、Spring コンテナに登録することを意味する。
メソッドの引数 A a は、Spring のコンテナから自動的に注入される。A が @Component アノテーションで定義されているので、Spring はそのインスタンスを作成し、run メソッドに渡す。
@Beanアノテーションは、@Configurationが付与されたJavaConfig内のインスタンスを生成するメソッドに付与するアノテーション。
@SpringBootApplicationアノテーション が付与されたクラス内で @Beanアノテーション を使うことで、Bean 定義を行い、依存性注入を受けることができる。
SpringApplication.run(MyApplication.class, args);
SpringApplication.run() メソッドは、Spring Boot アプリケーションを起動するための重要なメソッドで、Spring Boot アプリケーションコンテキストを初期化し、アプリケーションの実行を開始する。このメソッドは、次の主要な処理を行う。
処理 | 内容 |
---|---|
アプリケーションコンテキストの作成 | SpringのIoC(Inversion of Control)コンテナを作成する。このコンテナは、アプリケーション内のすべてのコンポーネント(Bean)を管理する。 |
コンポーネントスキャン | アプリケーションのパッケージをスキャンし、@Component、@Service、@Repository、@Controller などのアノテーションが付けられたクラスを自動的に検出して登録する。 |
アプリケーションの起動 | アプリケーションが実行可能状態になり、リクエストを受け取ったり、コマンドライン引数を処理したりする準備が整う。 |
SpringApplication.run() メソッドの引数については以下の通り。
引数 | 内容 |
---|---|
第1引数「MyApplication.class」 | Spring Boot アプリケーションのエントリポイントを示すクラス。このクラスには、@SpringBootApplication アノテーションが付けられている必要がある。このアノテーションは、Spring Boot アプリケーションの基本設定を自動的に行う。 |
第2引数「args」 | コマンドライン引数を受け取るための配列。コマンドライン引数は、アプリケーション実行時に外部から入力される情報で、特定の設定や動作を変更するために使用される。 |
SpringApplication.run() メソッドの実行フロー
フロー | 内容 |
---|---|
①アプリケーション起動 | SpringApplication.run() が呼ばれると、アプリケーションが起動する。 |
②コンテナの初期化 | SpringのIoCコンテナが初期化され、必要なBeanが作成される。 |
③コンポーネントスキャン | 指定されたパッケージ内のコンポーネントが検出されて、登録される。 |
④アプリケーションの実行 | アプリケーションが実行可能状態になり、リクエストの受け付けやコマンドライン引数の処理が開始される。 |
CommandLineRunner run(A a)
CommandLineRunner は、Spring Framework に含まれるインターフェースで、run(String... args) メソッドを持っている。このrun(String... args) メソッドは、アプリケーションが起動した時に呼び出される処理を定義する。
CommandLineRunner は、Spring Boot アプリケーションが起動した後に実行される処理を定義するためのインターフェースで、アプリケーションの起動時に特定のコードを実行できる。
CommandLineRunnerの引数: run メソッドの引数として、コマンドラインから渡された引数を受け取ることができる。これは、アプリケーションの設定や動作を動的に変更するために使われる。
5. 実行結果
このアプリケーションを実行すると、C の doSomething メソッドが呼び出され、以下の出力が得られる。
C is doing something.
6. CAlternativeの使用
もし CAlternative を使いたい場合は、B クラスを変更し、C の代わりに CAlternative を注入するようにする。
これを実現するために、プロファイルや条件付きで実装を切り替える方法もある。
例えば、@Qualifier アノテーションを使用して、特定の実装を選択することもできる。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
class B {
private final C c;
@Autowired
public B(@Qualifier("cAlternative") C c) { // CAlternativeを選択
this.c = c;
}
public void doSomething() {
c.doSomething();
}
}
まとめ
項目 | 内容 |
---|---|
DIコンテナ | オブジェクトの生成と管理を行うフレームワーク。Spring Bootは”DIコンテナ”を提供し、オブジェクトのライフサイクルを管理し、依存関係を自動的に解決する。 |
Bean | DIコンテナによって管理されるオブジェクト。 |
@Component | クラスをSpringのコンポーネントとして登録するアノテーション。 |
@Autowired | 依存関係を自動的に注入するためのアノテーション。 |
@Bean | 特定のメソッドの戻り値をSpringコンテナにBeanとして登録するアノテーション。 |
@SpringBootApplication | @Configuration, @EnableAutoConfiguration, @ComponentScan の3つでクラスをアノテートしたのと同じ扱いになる。 |
SpringApplication.run() メソッド | Spring Boot アプリケーションを起動するための重要なメソッドで、Spring Boot アプリケーションコンテキストを初期化し、アプリケーションの実行を開始する。 |
CommandLineRunnerインターフェース | Spring Boot アプリケーションが起動した後に実行される処理を定義するためのインターフェースで、アプリケーションの起動時に特定のコードを実行できる。 |
Discussion