【Java】DIコンテナとは何か(備忘録)

## はじめに
今回はDIコンテナについて説明します。

## DI(依存性の注入)のおさらい
まずDIコンテナの説明の前に、DIを利用したオブジェクト管理についておさらいします。DIにおけるオブジェクトの依存関係は以下の図の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3855381/57d7ea10-dbb8-655d-3347-008e6f0b5564.png)


ServiceクラスはAssistantRepositoryインターフェースに依存しています。AlexクラスやSiroクラスは、AssistantRepositoryインターフェースの実装クラスです。メインクラスからAlexクラスやSiroクラスを利用する場合、ServiceクラスでAlexクラスを生成し、Serviceクラスのメソッドを呼び出すことで各実装クラスのメソッドを利用していました。

DIのポイントは、メインクラスがAssistantRepositoryインターフェースに依存していないことです。これにより、実装クラスをAlexからSiroに変更しても、クラス変更の影響を受けないという利点があります。

## DIとDIコンテナの違い
DIでは、利用するオブジェクトに依存しない特徴がありますが、それにはメインクラスでAssistantRepositoryオブジェクトの生成や依存関係の注入を記載する必要があります。このオブジェクト生成や依存性の注入を自動化するのがDIコンテナの役割です。

## 実現例
具体的にステレオタイプアノテーションを使って注入するオブジェクトを宣言する方法があります。
今回使用するステレオタイプアノテーションは下記です。
- @Configuration・・・DIコンテナに読み込ませる情報を記載したクラスに付与するアノテーション
- @ComponentScan・・・宣言するステレオタイプアノテーションを探すためのアノテーション
- @Service・・・Serviceの具象クラスにつけるアノテーション
- @Repository・・・Repositoryの具象クラスにつけるアノテーション

以下にステレオタイプアノテーションを利用した例を示します。このプログラムは上記のクラス図を基にDIコンテナを実装したものです。
    
```java:Serviceクラス
package com.example.DIConTest.Service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.DIConTest.Repository.AssistantRepository;

@Service
public class AIService {
	private AssistantRepository AssistantRepository;
	
	@Autowired
	public AIService(AssistantRepository AssistantRepository) {
		this.AssistantRepository=AssistantRepository;
	}
	

	public void Hello() {
		AssistantRepository.Greeting();
}
	
}
```
@Serviceが付与されたクラスは、ServiceクラスとしてDIコンテナに読み込まれ、DIコンテナによって自動的にオブジェクト化されます。このオブジェクトはBeanと呼ばれます。


```java:Repositoryクラス
package com.example.DIConTest.Repository;

import org.springframework.stereotype.Repository;

@Repository
public class Alex implements AssistantRepository  {

 @Override
 public void Greeting() {
	 System.out.println("こんにちは。私は Alex です。");
	 System.out.println("Riverから生まれました。");
 }

}

public interface AssistantRepository {
	void Greeting();
}
```
@Repositoryが付与されたクラスは、RepositoryクラスとしてDIコンテナに読み込まれます。DIコンテナによって、Alexクラスは@Serviceが付与されたAIServiceクラスに注入されます。


```java:メイン
package com.example.DIConTest;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.example.DIConTest.Service.AIService;



@Configuration
@ComponentScan
public class DiConTestApplication {

	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(DiConTestApplication.class);   //・・・⓵
		AIService AIService =  context.getBean(AIService.class);  //・・・⓶
		AIService.Hello();//・・・⓷
	}

}
```
@Configurationが付与されたクラスは、JavaコンフィグとしてDIコンテナがその設定内容を読み込むクラスになります。
まず、①でDIコンテナを生成します。このとき、引数に@Configurationが付与されたクラスを渡す必要があります。
次に、②で必要なクラスを指定してDIコンテナから取得します。取得したクラスにはDIコンテナによって依存性の注入が行われます。そのため、AIServiceには@Repositoryが付与されたAlexクラスが注入されます。
最後に、③でAIServiceにはAlexクラスが注入されているので、`AIService.hello()`を実行するとAlexクラスの`greeting()`メソッドが呼び出されます。このようにしてDIコンテナを利用したオブジェクト管理が可能となります。


## おわりに
以上からDIコンテナを簡単にまとめると次の通りになります。

- ステレオタイプアノテーションを使ってオブジェクト管理をすること
- 自動で依存性の注入をしてくれること


誤り等あればご指摘ください。

## 参考文献
- 土岐孝平 著, "プロになるためのSpring入門 ――ゼロからの開発力養成講座",
技術評論社, pp.48-79