ことりんと一緒 - 5. Spring Boot

## 説明
Java で Webアプリケーションを開発する時、多くの場合は何かしらのフレームワークを使いますよね。
中でも **「[Spring Framework](https://projects.spring.io/spring-framework/)」** は多くの機能を取り揃えていて便利に開発ができますよね。

しかし、リリースが進むに連れて様々なコンポーネントが追加されて重厚長大になってきているのは確かです。新しく始めようと思う人には難しく思う人もいるかもしれません。

そんな敷居を取り去ってくれるのが、**「[Spring Boot](https://projects.spring.io/spring-boot/)」** です。

Spring Boot は、Spring Framework ベースのアプリケーションを構成する作業のほとんどを引き受けてくれる軽量なフレームワークです。
重厚長大となった Spring Framework を簡単に始める事ができます。

ところで、Spring Framework 5 では正式にコアの部分で Kotlin をサポートする事もあり、Kotlin を使って Spring Boot のアプリケーションを作ってみたいと思います。

## 前提
次の環境で作業しています。

|項目|内容|
|---|---|
|OS|MacOS|
|JDK|1.8.0_152|
|Gradle|4.3.1|
|Kotlin|1.2.10|
|Spring Boot|1.5.9|
|IDE|IntelliJ IDEA|

## 手順
### 1. build.gradle
まず、build.gradle を作成します。

#### 1.1. buildscript
Kotlin と Spring Boot はそれぞれ現在リリースされている最新版を設定します。

|設定項目|バージョン|
|---------|------------|
|Kotlin|1.2.10|
|Spring Boot|1.5.9.RELEASE|

```groovy
buildscript {
    ext {
        kotlinVersion = '1.2.10'
        springBootVersion = '1.5.9.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
    }
}
```

依存ライブラリとして **kotlin-allopen** を追加しています。
Kotlin で定義するクラスは、**final** 宣言されている状態になるため、デフォルトではSpring のアノテーションを用いた **Autowired** ができません。クラスを定義する際に **open 修飾子** をつけて継承・オーバーライド可能な状態にする必要があります。
全ての対象に open をつけるのは手間ですので、暗黙的に open がついている状態にするプラグインがあります。それが **kotlin--allopen** です。

#### 1.2. apply plugin
次に使用するプラグインを読み込みます。

```groovy
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
```

**kotlin-spring** により、Spring のアノテーションがついているクラスを open にしてくれます。

#### 1.3. JAR タスク
JAR タスクでアーカイブする JAR ファイル名を指定します。

```groovy
jar {
    baseName = 'kotlinboot'
}
```

ここでは、`kotlinboot` という名前にしています。

#### 1.4. 依存ライブラリ
Spring Boot と Kotlin を動作するのに必要なライブラリを設定します。
今回作成する Spring Boot アプリケーションは、Thymeleaf を使用して画面を作成してみようと思います。そこで、**org.springframework.boot:spring-boot-starter-thymeleaf** を追加しています。

```groovy
dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-devtools')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}")
    compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    testCompile('org.springframework.boot:spring-boot-starter-test')
}
```

### 2. Spring Boot Kotlin ソースコード
次に、Kotlin でSpring Boot のソースコードを作っていきます。

#### 2.1. SpringBootApplication メインクラス
@SpringBootApplication アノテーションをつけた起動クラスを作成します。

```kotlin
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
open class Application {
    companion object {
        @JvmStatic fun main(args: Array<String>) {
            SpringApplication.run(Application::class.java, *args)
        }
    }
}
```

Spring Framework のライブラリから、この main メソッドが static 呼び出しできるように以下の点を Java で作る時とは変更しています。

|#|変更点|内容|
|-|-------|-----|
|1| open 修飾子|ここでは付けているが kotlin-allopen を参照しているので実はつける必要はない|
|2|companion object|Java から Kotlin の 関数を Companionオブジェクトのメンバ関数として呼び出し<br>Application.Companion.main() として呼び出し|
|3|@JvmStatic|Companion オブジェクトのメンバ関数を static 呼び出し<br>Application.main() として呼び出し|

#### 2.2. Spring MVC コントローラ

```kotlin
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.RequestMapping
import com.shinyay.sample.springboot.demokotlin.model.fizzBuzzLambda

@Controller
class MainController {
    @GetMapping
    fun index(model: Model): String {
        val fizzbuzz = fizzBuzzLambda(50)
        model.addAttribute("message", fizzbuzz)
        return "index"
    }
}
```

@GeyMapping アノテーションをつけた関数で受けたリクエストのパスに応じてハンドリングします。ここではルートアクセスした際に index 関数が呼ばれています。
モデルにメッセージを詰めて、**index** を返しています。
これにより、**/resources/templates/index.html** Thmeleaf テンプレートファイルを参照します。

#### 2.3. Thmeleaf テンプレート
以下のテンプレートファイルに対して、コントローラーで生成したモデルに詰めたメッセージを展開して表示します。

```html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Sample Application</h1>
<pre th:text="${message}">message</pre>
</body>
</html>
```

### 3.  FizzBuzz
モデルにつめたメッセージは、Kotlin で FizzBuzz を実装した結果を出力するようにしています。

```kotlin
fun fizzBuzzLambda(n: Int): String {
    val fizz = { i: Int -> if (i % 3 == 0) "Fizz" else "" }
    val buzz = { i: Int -> if (i % 5 == 0) "Buzz" else "" }
    val number = { i: Int -> if (i % 3 != 0 && i % 5 != 0) i else "" }
    return generateSequence(1) { it + 1 }.map {fizz(it) + buzz(it) + number(it)}.take(n).joinToString("\n")
}
```

## まとめ
Kotlin でも Java と変わらずに Spring Boot でアプリケーションが作れそうな感触が分かりました。

### 今回のソース
- [shinyay/kotlin-basics 1.0.0](https://github.com/shinyay/kotlin-basics/tree/1.0.0/04-springboot)