はじめに
Springとは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サポート
- ソースコード
Spring Boot
- 2014年に1.0がリリース
- スタンドアロンのSpringアプリケーションが作成できる。
- AutoConfigurationにより自動的な設定をしてくれる。
- Spring InitializerはSpring Bootを前提にしている。
- ソースコード
Spring FrameworkとSpring Bootの違い
- Spring Frameworkは、Java言語のアプリケーション開発のために作られたフレームワークの集合体。
- Spring bootは、集合体となった機能を使いやすくするためのフレームワーク。
ここまではSpring FrameworkとSpring Bootの概要について紹介しました。では実際にコードに触れながら説明していきます。
SpringBootでアプリケーションを作成
REST APIが動くまで
今回はspring initializrを使ってプロジェクトを作成します。Spring InitializrとはSpring BootプロジェクトをGUI上で簡単に新規作成することができるツールです。今回は以下の設定でプロジェクトを作成します。また今回はコード短縮のためKotlinを使って説明します。
- Gradle(Groovy)
- Kotlin
- Spring Boot V3.1.2
- JDK17
- Dependencies
- Spring Web
早速作成されたプロジェクトをエディタやIDEで開いてみましょう。
build.gradleを確認すると作成されたプロジェクトの依存関係は以下のようになっていることがわかります。Spring Initializrで設定した通り依存関係にspring-boot-starter-webが設定されています。
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起動時に実行されるクラスです
package com.example.demo
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
次にwebアプリケーションを実行するためにコントローラークラスを作成します。
コントローラクラスはクライアントからのリクエストやレスポンスに関わる処理をするためのクラスです。以下の設定では/hello
にアクセスすると”hello”と返すようにしました。
package com.example.demo.controller
@RestController
class HelloController {
@GetMapping("/hello")
fun hello(): String {
return "hello"
}
}
これで準備は完了です。では早速アプリを起動しリクエストを送信してみましょう。
## 起動
./gradlew bootRun
## リクエストを送る
$ curl localhost:8080/hello
hello
controllerに記述したようにhelloが返ってきます。
なぜ動くのか
実際にアプリを起動しリクエストを送りましたが、コントローラクラスを書いただけでなぜレスポンスを返してくれるのでしょうか?
それはapplicationクラスの @SpringBootApplication
に秘密があります。
@SpringBootApplication
は複数のアノテーションの複合物で@EnableAutoConfiguration
や @ComponentScan
などのアノテーションを含みます。代わりに同じアノテーションをつけても動きます。詳しくはリファレンスに記載があります。
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で行われており、@ComponentScan
アノテーションが付いたクラスのパッケージ配下のコンポーネントを取り出しています。
そしてスキャンし集められたクラスはDefaultListableBeanFactoryでインスタンスが作成され、DIコンテナに登録されています。
AutoConfiguration
AutoConfigurationとはSpringが提供している各種プロジェクトの設定や3rdパーティ製のライブラリで必要な設定を、Spring Frameworkで使えるようにSpring BootがBean定義を自動的に行ってくれる仕組みです。
BeanとはDIコンテナに登録するコンポーネントのことです。(ここでのBeanはSpringのBeanを指し、Java Beansとは関係ありません。)
処理的には、まず@EnableAutoConfigurationの実装を見てみると、別のConfigurationクラスをインポートする@Import
が付いており、ここではAutoConfigurationImportSelectorが指定されています。AutoConfigurationImportSelectorでは、META-INF/spring.factoriesで指定されているそれぞれのライブラリで必要な設定を取得しています。これらのConfigurationクラスには前述のアノテーション表にもある@Configuration
が付いておりBean定義されます。
Mapping
最後に何故HelloContorollerクラスをDIコンテナに登録するだけで/hello
にリクエストがあった場合レスポンスを返せるのかについて説明します。
Spring Frameworkではマッピングは自動的にしてくれないのでSpring Bootが自動的に設定してくれています。webモジュールのAutoConfigurationを確認すると、WebMvcAutoConfigurationにcreateRequestMappingHandlerMappingというというものがあります。ブレイクポイントを張り処理を追いかけていくと最終的にMethodIntrospectorでパスと関数のマッピングを作っていることがわかります。
実際にデバッグして確かめてみました。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がそれぞれ何をしてアプリケーションが実行できているのか理解の役に立てば幸いです。
ありがとうございました。