ymlファイルから値を読み込む方法

本日も実務の実装の中で、学んだことがあったので備忘録として書き残しておこうと思います。

## このドキュメントについて

ymlファイル(外部ファイル)からデータを読み込む以下2つのやり方についてです。

1. @Valueを利用した読み込み方法
2. @ConfigurationPropertiesを利用した読み込み方法


## 実行環境

・Java17
・SpringBoot  3.0.2

build.gradleの設定は以下
``` java
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.0.2'
	id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-mail'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}
```

## 実装
applivation.ymlファイルには以下設定しておきます。(メール送信に使ってた)

```java
spring:
  mail:
    host: smtp.gmail.com
    port: 587
    username: dummy
    password: dummy

```
<br>

#### 1. @Valueを利用した読み込み方法

ymlファイルの値を単体で取得したい場合には、@Valueで良いと思う。

```java
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
@Getter
public class MailConfigValue {
    @Value("${spring.mail.port}")
    private int port;
}

```
説明：
1. 値を設定したいフィールドに@Valueを付与して、引数に設定したいymlファイルの値の変数名を階層ごとに.でつないで記載する。

2. ＠Componentは、アプリケーション起動時にこのクラスをDIコンテナに登録してくれるので、依存性注入を行うことでportの値を取り出せるようになる。

3. 取り出しには@Getterを付与しているので、getPort()でオッケー。


下記が例。

```java

@Service
@RequiredArgsConstructor
@Slf4j
public class MailSenderService {

    private final MailConfigValue mailConfigValue;

    public void smtpMailSender() {
        log.info(String.valueOf(mailConfigValue.getPort()));
        return true;
    }
}

```

<br>

#### 2. @ConfigurationPropertiesを利用した読み込み方法

値を複数まとめて取得したい場合には、@ConfigurationPropertiesを利用する。

```java

package com.example.demo.config;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;

@RequiredArgsConstructor
@Getter
@ConfigurationProperties(prefix="spring.mail")
public class MailConfig {
    private final String host;
    private final int port;
    private final String userName;
    private final String password;
}

```

説明：
1. データを設定するクラスを用意し、ymlファイルの変数と同じ名前の変数名を定義する。
2. @ConfigurationPropertiesを付与し、prefixに設定したい値がある階層まで記載する。
3. 注意したいのが、@ConfigurationPropertiesはsetterがないと、データを設定できないので、アプリケーションが起動しない。
→@RequiredArgsConstructorを付与して、コンストラクタで値を設定できるようにしてあげるか、lombokの@Data等付与してあげる必要がある。

4. @ConfigurationPropertiesは、@ConfigurationPropertiesScanを起動クラスに記載する必要がある。
これがないと、@ConfigurationPropertiesを読みにいかないので、アプリケーションが起動しない。

5. @ConfigurationPropertiesScanは、DIコンテナに登録も行ってくれるので、依存性注入を行うことでymlファイルの値が利用できるようになる。


下記のように、起動クラスに@ConfigurationPropertiesScanを設定する。

```java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@ConfigurationPropertiesScan
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

```

## まとめ
@Valueを利用するシーンはあんまりないかもしれない。
複数つけるような場面であれば、@ConfigurationPropertiesを利用すると思うし。。

setterが必要とかも知らずに、なんとなく既存コードを真似して書いていたので、よくないなと思いました。
しっかり、1つずつの意味を理解しながら実装するべきですね、、