初学者のためのSpring Framework & Spring Boot

## はじめに
[Spring](https://spring.io/)とはJavaアプリケーション作成の単純化を目指したプロジェクトのエコシステムです。"Spring"という言葉は Spring Framework を指し、Spring BootなどのSpringベースのテクノロジーを使用するあらゆるプロジェクトの総称です。この記事ではSpring FrameworkとSpring Bootについて紹介します。

## Spring Framework & Spring Boot
### Spring Framework
- 2004年に正式リリース。Javaアプリケーション用のフレームワーク。
- Inversion of Cotnrolが中心的な機能。 (いわゆるDIコンテナ)
- 2018年9月 Spring Framework 5.1から Kotlin 1.1サポート
- [ソースコード](https://github.com/spring-projects/spring-framework)

### Spring Boot
- 2014年に1.0がリリース
- スタンドアロンのSpringアプリケーションが作成できる。
- AutoConfigurationにより自動的な設定をしてくれる。
- Spring InitializerはSpring Bootを前提にしている。
- [ソースコード](https://github.com/spring-projects/spring-boot)

### Spring FrameworkとSpring Bootの違い

- Spring Frameworkは、Java言語のアプリケーション開発のために作られたフレームワークの集合体。
- Spring bootは、集合体となった機能を使いやすくするためのフレームワーク。

ここまではSpring FrameworkとSpring Bootの概要について紹介しました。では実際にコードに触れながら説明していきます。

## SpringBootでアプリケーションを作成

### REST APIが動くまで

今回は[spring initializr](https://start.spring.io/)を使ってプロジェクトを作成します。Spring InitializrとはSpring BootプロジェクトをGUI上で簡単に新規作成することができるツールです。今回は以下の設定でプロジェクトを作成します。また今回はコード短縮のためKotlinを使って説明します。

- Gradle(Groovy)
- Kotlin
- Spring Boot V3.1.2
- JDK17
- Dependencies
    - Spring Web
 
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267562/84106ce8-916b-bdfe-2521-42129c36da2a.png)


早速作成されたプロジェクトをエディタやIDEで開いてみましょう。

build.gradleを確認すると作成されたプロジェクトの依存関係は以下のようになっていることがわかります。Spring Initializrで設定した通り依存関係にspring-boot-starter-webが設定されています。

```groovy
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
	implementation 'org.jetbrains.kotlin:kotlin-reflect'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
```

では次にapplicationクラスです。このクラスは自動的に作成されます。
このクラスはmainメソッドが記述されたクラスでSpringBoot起動時に実行されるクラスです

```kotlin
package com.example.demo

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
	runApplication<DemoApplication>(*args)
}
```

次にwebアプリケーションを実行するためにコントローラークラスを作成します。
コントローラクラスはクライアントからのリクエストやレスポンスに関わる処理をするためのクラスです。以下の設定では`/hello`にアクセスすると”hello”と返すようにしました。

```kotlin
package com.example.demo.controller

@RestController
class HelloController {

  @GetMapping("/hello")
  fun hello(): String {
    return "hello"
  }
}
```

これで準備は完了です。では早速アプリを起動しリクエストを送信してみましょう。

```bash
## 起動
./gradlew bootRun

## リクエストを送る
$ curl localhost:8080/hello
hello
```

controllerに記述したようにhelloが返ってきます。

## なぜ動くのか

実際にアプリを起動しリクエストを送りましたが、コントローラクラスを書いただけでなぜレスポンスを返してくれるのでしょうか？
それはapplicationクラスの `@SpringBootApplication`に秘密があります。

`@SpringBootApplication`は複数のアノテーションの複合物で`@EnableAutoConfiguration` や `@ComponentScan`などのアノテーションを含みます。代わりに同じアノテーションをつけても動きます。詳しくは[リファレンス](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#getting-started.first-application.code.spring-boot-application)に記載があります。

> The second class-level annotation is `@SpringBootApplication`. This annotation is known as a *meta-annotation*, it combines `@SpringBootConfiguration`, `@EnableAutoConfiguration` and `@ComponentScan`.
> 

軽く紹介します。

`@ComponentScan` はSpring FrameworkがコンポーネントスキャンをしBeanをDIコンテナに登録するためのアノテーションです。よって起動時に`@RestController`の付いたHelloControllerインスタンスがDIコンテナに登録され、そのあとにAutoConfigurationでコントローラクラスの関数とパスのマップを作成します。リクエストがあった場合そのマップとパスを照らし合わせ、関数を呼び出しレスポンスを返すようになっています。細かく説明していきます。

## 各処理の詳細

処理の流れについては上記の通りですが、それぞれの処理が一体どのようなものなのかについて説明します。

### ComponentScan

ComponentScan とは、パッケージ配下のクラスをスキャンして特定のクラスを自動的にDIコンテナに登録することです。DIとはインスタンスを外部から渡して使用するクラス設計のことで、それらのインスタンスを格納する場所をDIコンテナと呼びます。
デフォルトでは以下のアノテーションが付いたクラスが対象となります。

| アノテーション | 説明 |
| --- | --- |
| @Controller | クライアントからのリクエスト/レスポンスに関わる処理をするコンポーネントであることを表すアノテーション。 |
| @Service | ビジネスロジックを実装するコンポーネントであることを表すアノテーション。 |
| @Repository | データの永続化に関わる処理を提供するコンポーネント |
| @Component | 上記3つに当てはまらないコンポーネント。 |
| @Configuration | Beanの設定を行うものであることを示す。Bean設定クラスには常にこれをつけます。 |
| @RestController | JSONやXML等を返すWebAPI用のコントローラに付与する。 |
| @ControllerAdvice | Controllerを横断して例外をハンドリングする場合、例外ハンドリング用のクラスにこのアノテーションを付与する。ExceptionHandler系のクラスに使用する。 |

よって先ほど作成したプロジェクトでは`@RestController`がついたHelloControllerクラスがスキャンされます。実際の処理はSpringFrameworkの[ConfigurationClassParser](https://github.com/spring-projects/spring-framework/blob/v6.0.11/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java#L281-L301)で行われており、`@ComponentScan`アノテーションが付いたクラスのパッケージ配下のコンポーネントを取り出しています。
そしてスキャンし集められたクラスは[DefaultListableBeanFactory](https://github.com/spring-projects/spring-framework/blob/v6.0.11/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java#L963-L976)でインスタンスが作成され、DIコンテナに登録されています。

### AutoConfiguration

AutoConfigurationとはSpringが提供している各種プロジェクトの設定や3rdパーティ製のライブラリで必要な設定を、Spring Frameworkで使えるようにSpring BootがBean定義を自動的に行ってくれる仕組みです。
BeanとはDIコンテナに登録するコンポーネントのことです。(ここでのBeanはSpringのBeanを指し、Java Beansとは関係ありません。)

処理的には、まず[@EnableAutoConfiguration](https://github.com/spring-projects/spring-boot/blob/v3.1.2/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/EnableAutoConfiguration.java#L82)の実装を見てみると、別のConfigurationクラスをインポートする`@Import`が付いており、ここでは[AutoConfigurationImportSelector](https://github.com/spring-projects/spring-boot/blob/v3.1.2/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java#L179-L187)が指定されています。AutoConfigurationImportSelectorでは、[META-INF/spring.factories](https://github.com/spring-projects/spring-boot/blob/v3.1.2/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories)で指定されているそれぞれのライブラリで必要な設定を取得しています。これらのConfigurationクラスには前述のアノテーション表にもある`@Configuration`が付いておりBean定義されます。

### Mapping

最後に何故HelloContorollerクラスをDIコンテナに登録するだけで`/hello`にリクエストがあった場合レスポンスを返せるのかについて説明します。
Spring Frameworkではマッピングは自動的にしてくれないのでSpring Bootが自動的に設定してくれています。webモジュールのAutoConfigurationを確認すると、WebMvcAutoConfigurationに[createRequestMappingHandlerMapping](https://github.com/spring-projects/spring-boot/blob/v3.1.2/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java#L542-L552)というというものがあります。ブレイクポイントを張り処理を追いかけていくと最終的に[MethodIntrospector](https://github.com/spring-projects/spring-framework/blob/v6.0.11/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java#L58-L85)でパスと関数のマッピングを作っていることがわかります。

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267562/a82bd832-f950-31cb-4c35-de3724dd7409.png)

実際にデバッグして確かめてみました。methodMapに`com.example.demo.controller.HelloController.hello():{GET[]/hello}`
が入っており、helloにGETリクエストがあった場合、このマップのパスを照らし合わせhello()を実行することがわかります。

## まとめ

ここまで記事を読んでくださった方は
- Spring Frameworkはコンポーネントスキャンでインスタンスを作成したり、DIコンテナへBeanの登録をしている。
- Spring BootはAutoConfigurationで各モジュールの初期設定などを行っている。

ということが分かったのではないでしょうか。Spring Framework,Spring Bootはかなり魔法のような部分が多くなぜ動いているのかわからないという方が多いと思います。この記事が少しでもSpring Framework と Spring Bootがそれぞれ何をしてアプリケーションが実行できているのか理解の役に立てば幸いです。
ありがとうございました。