しおしお

IntelliJ IDEAのことなんかを書いてます

KotlinでSpring Bootしてみた

Spring Bootってキワードよく聞くし、触ってみたくなったのでKotlinつかってちょっとだけ書いてみた。

やったこと

Hello World!」と表示するhtmlを返すだけ。Viewは「thymeleaf」を使っています。
一応テストコード的なものも書いてみました。

プロダクション側のコード

多分Javaで書いてもあんまり変わらない感じだと思います。

package siosio.application

import org.springframework.boot.SpringApplication
import org.springframework.context.annotation.ComponentScan
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.stereotype.Controller
import kotlin.properties.Delegates
import org.springframework.ui.Model

fun main(args: Array<String>) {
  SpringApplication.run(array(javaClass<Application>()), args)
}

[EnableAutoConfiguration]
[ComponentScan]
[Controller]
open class Application {
  private [Autowired] val hello: HelloService? = null

  [RequestMapping(array("/"))]
  fun home(model: Model): String {
    model.addAttribute("message", hello!!.getMessage())
    return "home/hello"
  }
}

[Component]
class HelloService {
  fun getMessage(): String = "Hello World!"
}

テスト側のコード

Modelにつめた値とViewの名称などをアサートしてみている。
xpathつかったアサートもできるっぽいけど確認できていないです。

package siosio

import org.junit.runner.RunWith
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.test.context.web.WebAppConfiguration
import org.springframework.test.context.ContextConfiguration
import siosio.application.Application
import org.junit.Test
import org.springframework.test.web.servlet.MockMvc
import org.junit.Before
import org.mockito.MockitoAnnotations
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import kotlin.properties.Delegates
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.*
import org.springframework.http.MediaType

[RunWith(javaClass<SpringJUnit4ClassRunner>())]
[ContextConfiguration(classes = array(javaClass<Application>()))]
[WebAppConfiguration]
public class ApplicationTest {

  private var mockMvc: MockMvc by  Delegates.notNull()

  Autowired val controller: Application? = null

  Before
  public fun setup() {
    MockitoAnnotations.initMocks(this);
    mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
  }

  Test fun testHelloWorld() {
    mockMvc.perform(get("/"))
        .andExpect(status().isOk())
        .andExpect(view().name("home/hello"))
        .andExpect(model().attribute("message", "Hello World!"))
  }
}

Kotlinでやった場合面倒だった点

可変長引数やアノテーションの属性

Javaの可変長引数やアノテーションの属性が配列の場合に、array関数で明示的に配列として設定してあげないとダメなのがちょっと面倒ですね(´・ω・`)

アノテーションの補完ができない

これSpring全く関係ないけど、アノテーション多用するSpringで補完できないのはつらみしかない。
一応この問題チケット登録されているので、そのうち対応されると思われます→https://youtrack.jetbrains.com/issue/KT-5992

Autowiredなプロパティの定義

必ずオートワイヤされるプロパティは、nullになることはないからnull不許可な型で宣言したいんだけど、下のコードみたいに初期化しないといけなくってnullで初期化することでnull許可の型になってしまうのが残念な感じがありますね。

private [Autowired] val hello: HelloService? = null

これを使う箇所では、下のコードのように「!!」でnullだったら例外ぶん投げてくれるような実装にしたりする必要があってかなりあれ。

model.addAttribute("message", hello!!.getMessage())

プログラム内で参照するフィールドをnull不可な型にするには、下のコードのようにオートワイヤされるnull許可フィールドとは別にプログラム内で参照されるnull不可なフィールドを定義する必要があります。このコードでは、プログラム内で参照されるフィールドのアクセッサーで、nullの場合にはAssertionErrorをぶん投げるようにして、null不可な型を実現しています。
こんな面倒くさいコードをオートワイヤされるフィールドに対して毎回書くとかアホらしいので、これだったらnull許可型で「!!」書いたほうが楽な感じありますね。

  private [Autowired] val _hello: HelloService? = null
  private val hello: HelloService
    get() = _hello ?: throw AssertionError()

  [RequestMapping(array("/"))]
  fun home(model: Model): String {
    model.addAttribute("message", hello.getMessage())
    return "home/hello"
  }

ソースコード全量

siosio/SpringBoot-Kotlin · GitHubにおいてあります。

追記・・・

以下のアドバイスを受けてオートワイヤをフィールドではなくコンストラクタで行うようにしてみました。
これだと、上に書いたnull許容型にしたりなどがなくなるのでいい感じですね。修正したコードが、githubにあげてあります。

Learning Spring Boot

Learning Spring Boot