@Beanと@Qualifierを使ってDIコンテナに設定したクラスを利用してみる

今日も備忘録です。

## このドキュメントについて
下記4点について記載していきます。

1. @Beanを利用して、DIコンテナにクラスを設定する方法
2. 上記で登録したクラスを利用する方法
3. @Qualifierを利用する必要がある場合について
4. @Qualifier利用する際の注意点

## 実行環境

・Java17
・springboot 3.0.2

## 実装
1. @Beanを利用して、DIコンテナにクラスを設定する方法
```java
@Configuration
public class ThreadPoolConfig {

    @Bean(name = "Thread1")
    ThreadPoolTaskExecutor configThreadPool() {
        var threadPool = new ThreadPoolTaskExecutor();
        threadPool.setCorePoolSize(2);
        threadPool.setMaxPoolSize(5);
        threadPool.setKeepAliveSeconds(0);
        threadPool.setQueueCapacity(500);
        threadPool.setDaemon(true);
        threadPool.setThreadNamePrefix("Thread1-");
        threadPool.initialize();
        return threadPool;
    }

    @Bean(name = "Thread2")
    ThreadPoolTaskExecutor configThreadPool2() {
        var threadPool = new ThreadPoolTaskExecutor();
        threadPool.setCorePoolSize(2);
        threadPool.setMaxPoolSize(5);
        threadPool.setKeepAliveSeconds(0);
        threadPool.setQueueCapacity(500);
        threadPool.setDaemon(true);
        threadPool.setThreadNamePrefix("Thread2-");
        threadPool.initialize();
        return threadPool;
    }
}

```
説明：
1. BeanをDIコンテナに登録するには、まず@Configurationを設定しする必要がある。
Spring Bootが起動時にDIコンテナに登録する対象を見つける際に@Configurationをつけたクラスを探してくれます。

2. @Beanをメソッドに付与して、登録したいクラスをreturnで返却する。
今回で言うと、threadPoolの設定を行ったThreadPoolTaskExecutorを返却している。

3. @Beanを付与する際に、name = "" をつけておくと、DIコンテナに登録する名前を決められる。


<span style="color: red; ">注意点</span>：
ここで同じクラスを登録する場合、上記のようにname = ""　をつけるまたは、@Primaryをつけないと、利用する際にspringがどちらのクラスを利用するのか判断ができなくなるので、アプリケーションの起動ができなくなる。

2.上記で登録したクラスを利用する方法
``` java
@Service
@RequiredArgsConstructor
@Slf4j
public class AsyncService {
    private final MailSenderService mailSenderService;
    private final UseThreadPoolTaskTest useThreadPoolTaskTest;

    @Autowired
    @Qualifier("Thread2")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    public void useThreadPoolTask(AsyncRequest request, LocalDateTime now) {
        threadPoolTaskExecutor.execute(() -> {
            log.info(Thread.currentThread().getName() + Thread.currentThread().getId());
            try{
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException("割り込み", e);
            }
            log.info("処理1終了");
        });

        threadPoolTaskExecutor.execute(() -> {
            log.info(Thread.currentThread().getName() + Thread.currentThread().getId());
            try{
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException("割り込み", e);
            }
            log.info("処理2終了");
        });

        threadPoolTaskExecutor.execute(() -> {
            IntStream.iterate(0, a -> a+1).limit(10).forEach(b -> log.info(String.valueOf(b)));
            log.info("処理3終了");});

    }
}


```

説明：
1. フィールドに記載したThreadPoolTaskExecutorに@Autowiredを利用して依存性を注入。
1. @Qualifierを付与して、先ほど@Beanに設定したname=""部分を引数に設定する
1. DIコンテナに登録されたクラスが利用できるようになる

<font color="Red">注意点：</font>
1.@RequiredArgsConstructorを利用して依存性を注入しようとして、finalをつけてしまうとコンストラクタにまで@Qualifierが設定されないので、@Autowiredで依存性を注入する。

↑これにハマって時間食ってしまったので、気をつける

## まとめ
@Configurationと@Beanの組合わせでDIコンテナに登録した後、@Qualifierを利用すると登録したクラスが利用できる。<br>
登録したクラスが単体で被りがないのであれば、@Qualifierを利用せず@RequiredArgsConstructorなどで依存性注入しても問題なく動く。

springの便利な@達なので、どんどん利用していきたいですね。