Springについて勉強したことまとめる#3(DI実装編)

こんにちは！
23卒新卒エンジニアのkamaです！
前回はspringで必須になってくる事前知識について取り上げました。

https://qiita.com/kama1021/items/d286cceb0977ae130d0d

今回は、その事前知識で学んだDIをどのようにSpringFrameworkを利用して実装するのか、という点に着目して、最小サイズのプログラムを作成していきたいと思います！！


### 本編前にちょっと
**#1**で取り上げた技術書の`Spring徹底入門`ですが、こちら読み進めていくうちに、初学者向けじゃない＋ハンズオン形式のものではなかったため、今回からは別の技術書を読みながら解説していきます！

https://www.shoeisha.co.jp/book/detail/9784798142470

今回から使っていく技術書は
**SpringFramework超入門
優しくわかるWebアプリ開発**

(株)フルネス 木下 雅章/著
出版社 : 技術評論社

https://gihyo.jp/book/2021/978-4-297-12429-8

です。
こちら、現在3分の1ほど読んでいるのですが、めちゃくちゃわかりやすいです。
専門的な用語や、難しい表現をなるべく省いているというのもあって、初学者でも理解しやすいと思いました。

:::note warn
最低限の知識として、ある程度の`オブジェクト指向`に対する理解と、`Java`の理解は必要なので、それらがまだな方は後回しのほうが良いかもです。
:::

::: note alert
また、この記事では、**最新の環境を用いた解説はしておりません！！**
教材にしている本で取り上げられているバージョンが古いというのもあるのですが、それ以前に、**私がアサインされた案件の要求バージョンがこの本よりさらに古い**というのがもっともな理由です。
具体的な環境は以下の通りです。
```
OS : windows11
Ecripseのバージョン : 2021-12
JDKのバージョン : JDK-11.0.19.7-hotspot
```
:::


## まずは環境構築
### Eclipseとは
なにはともあれ、環境構築が必要です。
Java単体を動作させるだけならVScodeで十分なんですけど、SpringFrameworkを利用するとなると、VScodeでは無理があります。

そこで登場するのが、Javaユーザーにはお馴染みらしい**Eclipse**です。
javaの統合開発環境の１つで、機能性の高さはもちろん、オープンソースなので、拡張機能やプラグインが豊富なのが特徴です。
使い勝手だけで言えば、C++、C#で言うVisualStudioにあたるのかな～と感じました。(もっともVisualStudioはオープンソースじゃないですけどね)

今回インストールして使っていくのは、この`Eclipse`にさらに便利な機能や日本語化を付け加えた、**Pleiades All in One**というパッケージになります。
JDKやその他諸々も一緒についてきて便利なので、これを使うのがおすすめです。

### インストールして起動
環境に合わせたものを以下からダウンロードします。
おそらく、ブラウザからセキュリティブロックを食らうと思いますが、無視しても問題ないです。

https://mergedoc.osdn.jp/#pleiades.html

ダウンロードが終わったら解凍するのですが、その際に、**なぜかWindowsデフォルトの解凍ではエラーが出てしまう**ので、[7zip](https://7-zip.opensource.jp/)などの外部のツールで解凍してください。
解凍先は任意のディレクトリで大丈夫ですが、なるべく浅い階層のほうが良いそうです。

解凍が完了したら、
```
任意のディレクトリ\pleiades{バージョン名}\pleiades\eclipse\eclipse.exe
```
を起動します。

起動すると、こんな画面が出ると思うので、赤枠の部分にチェックを入れて[起動(L)]を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/ca8350a1-a574-5b38-3804-f3e6276083e3.png)

無事、このような画面が表示されれば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/c914091f-ec7a-4811-f5b2-a5752c299cc0.png)

### プロジェクトの作成
実際に作っていくプロジェクトを作成できるまでが、環境構築です。(何かのインストールが足りてなくてプロジェクトが作れないパターンあり)
先ほどの画面から、
`ファイル(F) => Springスターター・プロジェクト(Spring Initializer)`
を選択する
この画面が表示されるはず。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/f2698720-874a-a8c9-190a-a32a9c79d9f3.png)


とりあえず、この記事で必要になる項目について説明します。
| 表示| 機能 |
|:-|:-|
|名前 |プロジェクトの名前。任意のものでOK(今回は`PrefecturesOfJapan`とした)|
|タイプ|ビルドツールの選択、とりあえずはGradleでOK(私もまだ理解しきれてないです)|
|Javaバージョン|読んで字のごとく、Javaのバージョン。この記事では11を利用するが、どのバージョンでもできると思う...|
|パッケージング|パッケージングの形式を指定する。ビルドするときに関係してくるが、とりあえずJarでOK|

他はデフォルトのまま次へ
依存関係を設定する画面に遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/e656c748-09cb-27fc-cfcb-0bacf77e4b06.png)
バージョンは任意のものに変更(今回は2.7.12)
いくつかデフォルトでチェックが入っているが、依存関係でエラーが出ると結構めんどくさいので、今回は必要な`Spring Boot DevTools`だけにチェックを入れて完了を押す。
無事デフォルトのプロジェクトが出来上がれば成功です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/e48ac503-8a5b-18c3-e1a6-4092d25419a0.png)

:::note
Springプロジェクトではファイル構造が複雑になりやすいので、わかりやすいように、
パッケージエクスプローラーの縦三点 => パッケージ・プレゼンテーション(P) => フラット(F)
にすると、めんどくさい構造部分をまとめて表示してくれるので便利です。
この記事でも、それをベースに説明が進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/d67c0ab8-9edd-93de-e8a0-6feaaaeaedd0.png)

:::


## DI実装！！
さてついにここからは実際にプログラミングしていくターンになります。
DI実装と聞いて難しいことをやるように感じるかもしれませんが、実際には、ApplicationContextが全部やってくれるので、こちらはむしろ簡単ですよ！
今回は、難解な処理はせず、シンプルに都道府県名をprintするだけのプログラムを作成します。

### クラスの確認
まずは大前提として、DIを実装するために必要な最低限のクラスについて確認して羅列しておきましょう。(その方が分かりやすいし、簡単)
#### インターフェースクラス
インターフェースを定義するクラスです。
今回の場合は、都道府県名をPrintするためのPrefecturesPrinter関数を抽象化しておきます。
#### 実装クラス
インターフェースを実装するクラスです。
今回はわかりやすいように、TokyoとOsakaの2つを作成することにしましょう。
#### 利用クラス
実際にインターフェースから注入されたインスタンスを利用するクラスです。
利用クラスは基本的にはエントリーポイントとなるmain関数があるクラスになると思いますが、今回作成したSpringスタータープロジェクトにはデフォルトで、`プロジェクト名+Application`というmain関数を持ったクラスが作成されているので、こちらを利用します。

### いざプログラミング
#### インターフェース
まずは、インターフェースクラスを作成しましょう。
パッケジエクスプローラーから階層を
`PrefecturesOfJapan => src/main/java => com.example.demo`
と下っていき、`com.example.demo`を右クリックして、
`新規(W) => パッケージ`
をクリックします。
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/7a71dddc-7e76-d065-cb09-503e5832a0b7.png)
この画面が表示されたら、名前の部分の後ろに`.used`を付けて、`com.example.demo.used`にしたら、[完了(F)]を押す。
すると新しくパッケジエクスプローラーにcom.example.demo.usedという**田**のようなアイコンのパッケージが作成されているはず。

そのパッケージを右クリックして、
`新規(W) => インターフェース`
でインターフェースを作成する
インターフェース作成画面に遷移したら、任意の名前を付ける。
ここでは`PrefecturesPrinter`とする
![image_720.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588962/e3f1e186-a3bb-afd6-49ae-4f5915e8fc3b.png)
[完了(F)]を押せばインターフェースクラスファイルが作成される。


作成されたファイルをクリックするとこのようなファイルになっているはず。
```java:PrefecturesPrinter
package com.example.demo.used;

public interface PerfecturesPrinter {

}
```

ここにPrefecturesPrint関数を抽象化しておいておきます。

:::note 
Javaでは、クラス宣言時に`interface`キーワードを付けることで、そのクラスで関数はすべて暗黙的に`public abstract`で宣言されたことになるので、抽象化に際して特別な記述はいらない。
:::

```java:PrefecturesPrinter
package com.example.demo.used;

public interface PerfecturesPrinter {
    /**
    *都道府県を出力する
    */    
    void prefecturesPrint;
}
```

:::note
Java(eclipseだけ？)では関数やクラスの直前に以下の書き方で書かれたコメントアウトを記入すると、他のファイルなどで、そのクラスや関数にホバーしたときにコメントを表示させることができる。(C#のXMLドキュメントみたい)
```java
/**
* ここが表示されるよ<br>
* 改行タグは<br>だよ
*/
```
:::

#### 実装クラス
ここまでは、ただのJavaでもやる範囲(のはず)。
ここからは、DIを実装するうえで大切なアノテーションを使って、記述していきます。

先ほどインターフェースクラスを作成したパッケージに実装クラスも作ってしまいます。(ほぼ操作は同じなので写真は省略)

`ファイル => 新規(N) => クラス`
でクラス作成画面へ遷移します。

今回も名前だけ変更で大丈夫です。
この手順で`Tokyo`と`Osaka`を作成しましょう！

作成で来たら、まずは普通に実装クラスを書いてみます。
ちなみに、`import`は必要に応じてeclipseが自動的に挿入削除してくれるので、気にしなくても大丈夫です！

```java:Tokyo
package com.example.demo.used;

public class Tokyo implements PerfecturesPrinter{
	public void prefecturePrint() {
        System.out.println("東京です");
	}
}
```

もちろん、これだけでもしっかりインターフェースの実装はできています。
が、ApplicationContextの恩恵を授かるためには、アノテーションで、そのクラス、関数がどういうものなのかを判別して管理してもらう必要があります。
そのためには、
##### 実装クラスには`@Component`
`@Component`自体は、Springでコンポーネントとして利用したいオブジェクトになら、何にでも付けれらます。
実装クラスに着けることで、ApplicationContextが自動的に依存関係を確認して、そのクラスが、どのインターフェースの実装をしたかまでを把握してくれて、なおかつインスタンスの生成も自動的にやってもらうように任せることができるようになります！

<details><summary>ちなみに</summary><div>

`@Component`は**インスタンス生成アノテーション**と呼ばれるものになるのですが、このインスタンス生成アノテーションは、他に3種類あります。
それぞれレイヤによって分けることが推奨されているようですが、今はまだ意識しすぎなくてもいいのかな、とも感じます(下手に意識しすぎると、余計に設計がごちゃごちゃする)
|アノテーション|レイヤ|概要|
|:-           |:-|:-|
|`@Controller`|アプリケーション層    |クライアントとのデータの入出力|
|`@Service`   |ドメイン層            |アプリケーションの中核で、業務処理を提供する|
|`@Repository`|インフラストラクチャ層|データベースへのデータ永続化、データアクセス処理|
|`@Component` |サブ処理             |それ以外のインスタンス生成|

クライアントからデータベースにアクセスする際、上のレイヤ順番に処理されていく。
ただし、`@Component`だけは、どの処理層でもサブ処理として並列的に処理される。
</div></details>

##### オーバーライドした関数は`@Override`
読んで字のごとくですが、オーバーライドした関数は`@Override`を付ける必要があります。

これらを正しく付けるとこうなります。(import部分は省略)
```java:Tokyo
package com.example.demo.used;

@Component
public class Tokyo implements PerfecturesPrinter{
    @Override
	public void prefecturePrint() {
        System.out.println("東京です");
	}
}
```

これでTokyoクラスが完成しました！
この調子でOsakaクラスも作っちゃってください！

#### 利用クラス
利用クラスは先ほども書いたように、最初から作成されている、`プロジェクト名＋Application`クラスを利用します。

まずは、インスタンスの生成をするために必要な、`@Autowired`の説明です。
このアノテーションが付与されたフィールドやメソッドには、自動的に適切な依存オブジェクトを注入してくれます。
つまり、**利用するクラスでnewしてインスタンスを作成することなく、実装クラスのインスタンスを利用することができる**ということです。
```java:PerfecturesOfJapanApplication
@AutoWired
Tokyo tokyo;
@AutoWired
Osaka osaka;
```
:::note warn
アノテーションは1つのオブジェクトにつき1つ必要なことに注意
:::

続いてオーバーライドした関数を呼び出す関数を作ります。

```java:PerfecturesOfJapanApplication
private void Printer(){
    tokyo.prefecturePrint();
    osaka.prefecturePrint();
}
```
ここは特に問題ないですね。

最後に、この関数を呼び出すエントリーポイントを作ります。
とはいっても、エントリーポイントとなるのは、デフォルトで作られている`main関数`になるので、ここに追記していきます。
ファイル作成時点で、main関数がこのようなつくりになっているはずです。
```java:PerfecturesOfJapanApplication
public static void main(String[] args) {
		SpringApplication.run(PrefecturesOfJapanApplication.class, args);
	}
```
まずは、現段階で書かれている内容の説明をします。
何やら長々と書かれていますが、やっていることは単純なことです。
このアプリケーションを`SpringApplication`という仕組みの、`run()`というかっこ中を実行するというメソッドを利用しています。
今回は、かっこの中が`PrefecturesOfJapanApplication.class`なので、この宣言がなされているクラスそのものをSpringApplicationとして実行するといった具合でしょうか。

しかし、これだけでは処理が書かれている関数は呼ばれていないので、実行しても何も起きません。(エラーも発生しません)
そのため、このクラスの、どの関数を実行するのか、という宣言が必要になります。
```java:PerfecturesOfJapanApplication
public static void main(String[] args) {
        //わかりやすいように改行してありますが、別に改行はなくても大丈夫です
		SpringApplication.run(PrefecturesOfJapanApplication.class, args)
        .getBean(PrefecturesOfJapanApplication.class).Printer();
	}
```
先ほどのコードの後ろに、`.getBean(PrefecturesOfJapanApplication.class).Printer()`という処理が追加されました。
これは、`PrefecturesOfJapanApplication`というBean(インスタンス)をApplicationContextから取得し、そのインスタンスの`Printer()`という関数を呼び出しています。

これで、一通りのコーディングが完了しました。

これを実行するには、
`パッケジエクスプローラー => エントリーポイントとなるjavaファイルを右クリック => 実行(R) => 3 Spring Boot アプリケーション`
でコンソールに結果が出力されます。

```console:コンソール
10:30:41.404 [Thread-0] DEBUG org.springframework.boot.devtools.restart.classloader.RestartClassLoader - Created RestartClassLoader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@153f309

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.12)

2023-05-24 10:30:41.693  INFO 15228 --- [  restartedMain] c.e.demo.PrefecturesOfJapanApplication   : Starting PrefecturesOfJapanApplication using Java 11.0.13 on ｛PCの情報とか｝
2023-05-24 10:30:41.694  INFO 15228 --- [  restartedMain] c.e.demo.PrefecturesOfJapanApplication   : No active profile set, falling back to 1 default profile: "default"
2023-05-24 10:30:41.734  INFO 15228 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2023-05-24 10:30:42.123  INFO 15228 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2023-05-24 10:30:42.138  INFO 15228 --- [  restartedMain] c.e.demo.PrefecturesOfJapanApplication   : Started PrefecturesOfJapanApplication in 0.72 seconds (JVM running for 1.478)
東京です
大阪やで

```

:::note warn
実行する際、右クリックするのは、プロジェクトそのものではなく、エントリーポイントとなるjavaファイルであることに注意(デフォルトのままなら、`プロジェクト名＋Application`)
:::

ここでエラーが発生する場合はどこかしらに間違ってしまったところがあるのでしょう。
ここで逐一エラーの説明をしてもしょうがないので、自分で調べる能力を磨きましょう。

## あとがき
前回まで使っていた技術書から一度離れて、ハンズオン形式の初学者向けの技術書を読み始めたのですが、大正解でした。
前回までは、結構難しいイメージしかなくて、どんよりだったんですけど、新しい技術書で、この記事でやった内容が理解できるようになってから、かなり学ぶのが楽しくなってきました！

まだまだチュートリアルの段階ではあると思いますが、しっかり自分に合った学び方でモチベをキープして勉強し続けれるのが大事だと改めて感じることができてよかったです。