【#1.SpringBoot】SpringBootとは？　〜DIについて〜[WIP]

#はじめに

この記事では、「Spring Boot」を利用したアプリケーション開発・構築において、
最初に引っかかるであろう「DI」、「AOP」についてまとめています。
今回は１章目として、DIについてまとめていきます。

今回の記事は、奥深くまで追求するというより、SpringFramework,SpringBootを触り始める
導入としての記事になります。

※Javaの一般的な知識や、SpringFrameworkについては理解がある前提で記事をまとめますが、
 できる限り初学者に分からなさそうな部分は、掘り下げて説明をしていきます。

#目次
　1.SpringBootって？
　2.SpringFrameworkとは？
　3.DIとは？
　4.DIとDIコンテナ

#1.SpringBootって？

SpringBootとは、Springアプリケーション（SpringFramework）をより簡単な、設定や準備、簡潔なコード（アノテーション）で
効率的に開発＋デプロイができるようになる、フレームワークと考えてください。
Springフレームワーク　のための　フレームワークといった立ち位置のイメージです。

上記のように、SpringFrameworkをより良く利用しやすくなるといった点から、
　・SpringFrameworkはどんなものか？
　・SpringFramework単体に比べ、どのような良さがあるのか？（ここは総じて、次回の記事でまとめる）
といった点を理解するのが、SpringBootの理解に繋がるかと思うため、
SpringFrameworkに関してまとめる。


#2.SpringFrameworkとは？

Javaのフレームワークの一つで、DIやAOPといった仕組みが導入されている、部分が大きな特徴です。
また、SpringFrameworkを軸として、SpringCloud,SpringBatch,SpringShell,SpringSecurityのような
多種多様に渡る汎用的に利用可能な、プロジェクトも提供されているのも大きな特徴です。
SpringFrameworkの教育コストをかけておくことで、将来的にいろんなシステムパターン（バッチ等も含めて）
構築できていく部分は、利点かと思います。

いや、そもそも...DIや、AOP....聞いたことあるけど、良くわかってない...というのが大半だと思うので、
その辺りも含めて、話をしようと思います。
今回は”DI”についてまとめていきます。

#3.DIとは？
DependencyInjectionの略で、DIと言います。
日本語の意味としては、「依存性の注入」と表現します。
いや、意味わからんねん。ってのが感想だと思います。笑

私も、この記事をまとめるにあたって、自己の認識が誤ってる部分も含めて
再学習を行いましたが、以下の記事がとてもわかりやすく、
こちらの表現だと、「オブジェクトの注入」という考え方がしっくりきます。
>https://qiita.com/ritukiii/items/de30b2d944109521298f

では、このDIとは実際にどんなものかを見ていきましょう。
以下２つが分かれば、ある程度DIについては理解できるかと思います。

1.従来どんな形式だったコードを、DI使うとどんなコードになるのか？
2.DIを利用することによって、どんな利点があるのか？
を確認していきましょう。


まずは、DIを利用しないパターンのサンプルコードをSpringBootアプリで作ってみます。
以下のリポジトリに置いているので、java11,mavenインストールしてる方は、ご自由にどうぞ。
https://github.com/sasdos-engee/Practice-DI


今回作成するsampleコードは、以下のようなrestAPIをイメージしています。

◽️仕様
機能：「POSTで受けたリクエストを元に、学生情報を登録するAPI」

MVC系のクラス
・Controller　・・・　リクエストを受けて、サービス層を呼び出し、結果に応じてレスポンスを返す。
・Service　・・・　業務的なチェックを実施後、登録処理を行うためrepository層呼び出し。
・Repository　・・・　DB登録処理を実施。

モデル系のクラス
・Student ・・・ 今回登録する単位となる、学生クラス
・request　・・・　POSTでリクエストを受ける際のオブジェクト
・response　・・・　レスポンスのオブジェクト


---
DIを利用しないパターン
---
対象のクラスは以下です。
　・SampleNonDiController
　・SampleNonDiSerivce
　・SampleNonDiRepository
　・RegistStudentRequest
　・RegistStudentResponse
　・Student

今回は、DIを使わない・・・＞インスタンスは全て各クラスの中でインスタンス化しています。



さあ、ぱっと見る感じ、これでも動きそう。
はい、動きます。　ではどんなケースに困るのか？

ControllerとService層、Repository層がそれぞれ違うメンバーで
実装していたと仮定しましょう。
それぞれのメンバー（A~Cさん）は、自分の担当層の実装、およびUTの実装を行います。

AさんのUTに、フォーカスして考えてみましょう。

AさんがUTにて担保するのは、
　・リクエストに対して、想定通りのオブジェクトを引数に、service層を呼び出せるか？
　・service層の戻りパターン（今回だと1 or 0）に応じた正しいレスポンスを返せるか？
である。　それは即ち、service層の振る舞いに左右されることなく、想定の結果が得られるか？
=> service層をモック化してテストを実施することになると思います。

今回Service層の実装が完了していないとして、モック化を考えてみましょう。
・・・と調べてみると分かると思います。
基本的には、JUnit等の実装にて、「メソッド内でインスタンス化している別クラスはモック化”できません”」。

例外的に、JMockitoライブラリや、PowerMockライブラリでモック化はできないことはないですが、
それぞれのライブラリ内の機能が非推奨や、利用できなくなることが多いうえに、
利用しているJUnit,Spring等のフレームワークバージョンとの相性等も考える必要があるため、
できれば利用したくないです。
よっぽどのことがなければ、上記のようなライブラリを使わずとも済むような設計でいきたい。

「そうなると....メソッド内でのインスタンス化を辞めるしかない。」にたどり着きます。
打開策として、newしているものを、引数として実態を渡してもらうこととしましょう。
service層にフォーカスして実装を組み替えてみましょう。

---
引数としてインスタンスを渡してもらう方式にしてみる
---
対象のクラスは以下です。
　・SampleNonDiVer2Controller
　・SampleNonDiVer2Serivce
　・SampleNonDiVer2Repository

Serviceクラスをみると、メソッド内でのインスタンス化がなくなりましたね。
これで、Service層のモック化が可能になりました。（引数のrepositoryInstanceの実態を、適宜
テスト側で変更することで、可能）

おっ、これで問題解決では？と感じますが、これではダメです。
なぜなら、Service層だけを見ると解決ですが、Controller層側の問題が深刻になります。
Service層を利用する側のController層が
Repository層のインスタンスも用意する必要が出てきて、いよいよカオスになります。


こうなってくると、serviceクラスのコンストラクタで、repository層のインスタンスを
設定するようにしたとしても、結局呼び元（Controller）でのインスタンス生成が避けれません。
これでは、クラスの依存階層が深ければ深くなるほど、呼び元でのインスタンス定義が増える一方で
根本解決になっていません。

そうなってくると、 Controller層で意識するServiceクラスのインスタンスを
適宜入れ替える仕組み（注入）がないと、UTも含めて根本解決できない。
同じように、Service層で意識するRepositoryクラスのインスタンスも同じです。

ここで出てくるのが、DI（依存性の注入＝オブジェクトの注入）です。

---
DI利用の場合
---
対象のクラスは以下です。
　・SampleDiController
　・SampleDiSerivce
　・SampleDiRepository

おいおい、アノテーション（@)付けるだけでよしなにやれてるの？って感じです。
そうです、これがDIになります。

コード的にはこれで、うまくやれてんだと分かりますが、中身が意味わかんないですね。
なので、DIについてももう少し掘り下げましょう。
DIを実現するにあたって必要な”DIコンテナ”という仕組みを覚えましょう。


#4.DIとDIコンテナについて
DIは上記で作成したコードのように、
　・クラス間での依存性の排除（厳密には、各クラスのメソッド内でのinstance化の排除）
　・依存していたクラスの注入（オブジェクトの注入）
することで実現できました。（できたようです）

みた感じ、各クラスに @Serviceやら@Repositoryをつけたり、
依存していたクラスを @Autowiredをつけた、privateフィールドで定義しているくらいですね。
これらが、実際に何をしているのか含めて見ていきましょう。


######DIコンテナについて
上記DI利用の場合のソースを見てみましょう。

SampleDiControllerのソース内のprivateフィールド
"sampleDiSerivice"に着目してみましょう。
今回、sampleDiServiceをメソッド内から外だしすることで、DI化に成功しました。
※上記ソースでは、SpringFrameworkを使ったDIコンテナを利用したDIになってます。
　（説明上分かり辛いのは、お許しを。）

しかし、プライベートフィールド化できたところで、
結局インスタンスの実態をクラスを利用する側が、生成する必要があります。
今回の例だと、SampleDiControllerを利用するモジュール側で、
SampleDiControllerのコンストラクタを利用する等して、sampleDiServiceのインスタンスを渡す等ですね。

それに、今回はSampleDiServiceの一つだけですが、利用する依存クラスが多いほど
DI時に呼び元が渡すインスタンスの数は膨大に増えていきます。
（コンストラクタにどんどん引数増えます）

それは嫌だな...なんか簡単にやる方法ないのかな.....と編み出されたのが、
”DIコンテナ”の仕組みです。

DIコンテナは一言で表すと”インスタンス生成＋よしなにインスタンスを渡す（DI)”機能を持ったものです。

######SpringFramework(Boot)での DI　と　DIコンテナについて
上記で説明したように、
DIコンテナに　インスタンス生成や、よしなにインスタンスを渡す（DIする）ために、
DI対象のクラスを宣言する必要があります。

それらを実施しているのが、
@Component　および　@Controller,@Service,@Repositoryのアノテーションです。

私自身も学習する中で、以下の記事が一番理解しやすかったです。
>https://blog.mookjp.io/blog-ja/spring-framework%E3%81%A8di%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/

それぞれのアノテーションについて説明をしていく。
@Controller、@Service,@Repositoryは、＠Componentを継承した形で　アーキテクチャに則ったアノテーションになっているので、
@Componentについて説明する。
このアノテーションをつけることで、”コンポーネントスキャン”という機能により、DIコンテナにBean登録されます。
これにより、DIコンテナ内にシングルトン（※最後の項で説明します）なクラスとして、インスタンスが登録されているという訳です。

では、DIコンテナからインスタンスを取り出して、注入してくれるのは誰か？
そうです。@Autowired　アノテーションです。
このアノテーションを利用することで、DIコンテナに登録されているインスタンスを、よしなに注入してくれます。

これで、SpringでのDI,DIコンテナの仕組みは、大体理解できたかと思います。

「おおお！じゃあ、基本的に独自クラスは、全部　@Componentつけて、　@Autowired　つけまくれば
　メソッド内でnewせずとも、動作できるんですね！！！」
と思うところですが、１点危険な点があるので注意しておきましょう。


######DIの注意点（シングルトンについて）
上記のように、@AutowiredをつけてDIできることが分かったと思いますが、
これらの”DI”はDI対象のクラスが　「シングルトン」というものになってます。

このシングルトンとは、インスタンスが１つしかないことを意味しています。
つまり、A,B,Cのクラスに対して、　DというクラスをDIした場合、
A,B,Cで利用されているDのインスタンスは同様のものということになります。
当然、Dの中で保持している、フィールドも共有ということになります。

今回、例示しているソースで説明すると、
StudentというクラスをDI対象にすると、
Studentクラス内の”number","name","furigana"のそれぞれの値を共有することになります。

ん？何か起きるの？悪いことがあるの？　＝＞　はい。設計次第では致命的です。

どんなことが起きてしまうのか？については、以下がわかりやすくまとめてありました。
>https://qiita.com/NagaokaKenichi/items/34bc62e572256c5710e1

何も考えずにDI対象のクラスが、状態や変数を持っておくと
スレッドセーフな作りにならず、WebAPIとして致命的な作りになってしまう可能性があります。
（Aさんの個人情報が、Bさんに見えてしまったりするよ）

なので、DIを利用する場合は、以下のどちらかの方針で気をつけましょう。

　案1.各スレッドで共有すると困るような、フィールドはクラスに持たせない作りにする。
　　（いわゆる、スレッドアンセーフになってしまうような変数をそもそもDI対象には持たせない。）

　案2.@Scopeを用いて、インスタンスのスコープを変更する。
　　　SpringのDIでは、デフォルトがシングルトンになっているため、 @ScopeアノテーションをDI対象のクラスにつけて、
　　　スコープの指定が行えます。
　　　対象クラスの呼び出しの度、　セッション毎、　リクエスト毎等、複数のスコープ設定が存在するため、
　　　各スレッドで競合が起きてしまわないような、スコープ定義を行ってスレッドセーフな作りにする。
　　　
案2は、シングルトンにしない時点で、多数のインスタンスを作り上げることを考えると、
できる限り、設計でスレッドセーフを回避して、案１によせるほうがいいのか？という印象を受けました。



ここまでで、DIについては粗方理解できたかと思います。
次回は、AOPと、SpringBootの特徴についてまとめていこうと思います。