LoginSignup
46

More than 5 years have passed since last update.

Kotlin で Spring Boot アプリを書く

Last updated at Posted at 2015-11-24

IntelliJ IDEA で「Kotlin の設定反映する?」みたいなのが出てきたので、試しに OK してみると build.gradle に設定追加してくれました。しかも特に違和感なくすぐに Kotlin を書き始められる。とてもスムーズ。

buildscript {
    ext.kotlin_version = '1.0.0-beta-2423'
    repositories {
        jcenter()
        maven { url "http://repo.spring.io/snapshot" }
        maven { url "http://repo.spring.io/milestone" }
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.0.RELEASE")
        classpath("org.springframework:springloaded:1.2.4.RELEASE")
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

// apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'spring-boot'
apply plugin: 'idea'

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("$buildDir/classes/main/")
    }
}
jar {
    baseName = 'myproject'
    version = '0.0.1-SNAPSHOT'
}
repositories {
    jcenter()
    maven { url "http://repo.spring.io/snapshot" }
    maven { url "http://repo.spring.io/milestone" }
    mavenCentral()
}
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}
sourceSets {
    main.java.srcDirs += 'src/main/kotlin'
}

上記の build.gradle は全く自分では設定していなくて、元々あった Spring Boot の設定に IDEA が勝手に追記してくれたもの。Spring Loaded もちゃんと Kotlin のコンパイルを IDEA でするとリロードされます。

ご存知の方も多いかと思いますが IDEA には Java コードをコピペして Scala コード中に貼り付けると勝手に Scala のコードに書き換えてくれるという神機能があって、これは JetBrains 謹製の Kotlin でも当然のように健在でした。

この記事で書いた Application.java を貼り付けると
http://qiita.com/seratch@github/items/5b4cf1194c3bbd9b55b0

こんな感じになります。

package friendlist

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration

@Configuration
@EnableAutoConfiguration
@ComponentScan
object Application {
    @JvmStatic fun main(args: Array<String>) {
        SpringApplication.run(Application::class.java, *args)
    }
}

一見良さそうですが Kotlin の class や object は open がついていないと final 宣言になるようで Spring Boot が起動時に「final やめろ」と起動エラーにしてきます。

Kotlin 不慣れながら見よう見まねでやってみて、以下のようにすると動作しました。
https://kotlinlang.org/docs/tutorials/

package friendlist

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration

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

Controller はこんな感じに書いてみました。

package friendlist.web

import friendlist.model.Friend
import friendlist.model.FriendResponse
import friendlist.model.FriendsResponse
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class FriendsController {

    @RequestMapping("/")
    fun home(): FriendsResponse {
        return all()
    }

    @RequestMapping("/friends")
    fun all(): FriendsResponse {
        return FriendsResponse(Friend.findAll())
    }

    @RequestMapping("/friends/{id}")
    fun show(@PathVariable id: Int): FriendResponse {
        return FriendResponse(Friend.findById(id))
    }

}

model と DAO はこんな感じ。static を使ってみたかったのでこうしてみただけというサンプルです。JvmStatic をつけてもつけなくても動作するようだけど Kotlin の中で扱う分にはどっちでもいいのかな?(よくわかってない)

package friendlist.model
data class Friend(val id: Int, val name: String) {
    companion object {
        private val ALL_FRIENDS: List<Friend> = listOf(
                Friend(1, "Alice"),
                Friend(2, "Bob"),
                Friend(3, "Chris"),
                Friend(4, "Denis")
        )
        fun findAll(): List<Friend> {
            return ALL_FRIENDS
        }
        fun findById(id: Int): Friend? {
            return ALL_FRIENDS.find { f -> f.id == id }
        }
    }
}
package friendlist.model
data class FriendResponse(val friend: Friend?)
package friendlist.model
data class FriendsResponse(val friends: List<Friend>)

アクセスしてみるとレスポンスボディはよしなに JSON になりました。Kotlin の List とか Friend? とかそのまま渡してみてもよしなに扱ってくれているようで、Scala と比べると Java との interoperability はかなりスムーズですね。

$ curl -v localhost:8080/friends
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /friends HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 24 Nov 2015 15:03:48 GMT
<
* Connection #0 to host localhost left intact
{"friends":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},{"id":3,"name":"Chris"},{"id":4,"name":"Denis"}]}

$ curl -v localhost:8080/friends/1
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /friends/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 24 Nov 2015 15:03:50 GMT
<
* Connection #0 to host localhost left intact
{"friend":{"id":1,"name":"Alice"}}

$ curl -v localhost:8080/friends/123
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /friends/123 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 24 Nov 2015 15:03:54 GMT
<
* Connection #0 to host localhost left intact
{"friend":null}

余談ですが Qiita の pre 記法で Kotlin のシンタックスハイライト提供されていますが、まだ companion object { } とかうまく認識できていないですね。とりあえず Java にしておきました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46