LoginSignup
9
14

More than 5 years have passed since last update.

KotlinでSpring Boot2

Last updated at Posted at 2018-12-19

サーバサイドKotlin、流行ってますね!!(?)
Kotlin用フレームワークは黎明期なので、Javaなフレームワークを選択する事になると思います。
Javaフレームワークと言ったらSpringFrameworkですよね。
KotlinでSpringBoot2をやってみたです。

サンプルでDBからデータと取得し返すRestAPIを作成します。

Projectを作成する

https://start.spring.io/
Spring InitializrでProjectを作成します。
Spring Initializr.png

DLしたプロジェクトをIntelliJ IDEAなどで開きます。

お好みですが、APサーバをTomcatではなく、Jettyとかを使用したいときは、dependenciesにJettyを追加し、spring-boot-starter-webに含まれているTomcatを除外します。

build.gradle
implementation('org.springframework.boot:spring-boot-starter-jetty')
build.gradle
configurations {
    implementation.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}

jdbcを入れているのでDriverが起動時に必要です。dependenciesに追加します。

build.gradle
runtime('com.h2database:h2')

これもお好みですが、application.propertiesではなくapplication.yml使用したい場合は、 application.propertiesを削除してapplication.ymlを作成します。

application.yml
## 例です
logging:
  level:
    root: warn
    org:
      springframework:
        web: info
spring:
  main:
    banner-mode: "off"

Controllerの作成

まず、簡易なAPIを作成します。
Annotationも気にせずに使用できます。

DemoRestController.kt
import com.fasterxml.jackson.annotation.JsonProperty
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping(value = ["/api"])
class DemoRestController {

    @GetMapping(
            value = ["/"],
            produces = [MediaType.APPLICATION_JSON_VALUE]
            )
    fun index(): IndexJson {
        return IndexJson("hogehoge", 12345)
    }
}

data class IndexJson(
        @JsonProperty(value = "name") val name: String,
        @JsonProperty(value = "no") val no: Int
)

Data Classを、そのまま返すだけなのでお手軽ですね。

起動

$ ./gradlew bootRun
$ curl -D - http://localhost:8080/api/
HTTP/1.1 200 OK
Date: Wed, 19 Dec 2018 10:30:50 GMT
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked

{"name":"hogehoge","no":12345}

はい。Jsonで返ってきました。

DB接続

H2の定義を追加します。

application.yml
spring:
  main:
    banner-mode: "off"
  datasource:
    url: jdbc:h2:/tmp/h2/demo
    username: sa
    password:
  h2:
    console:
      enabled: true

アプリケーションを再起動すればH2のWebコンソールが起動します。
http://localhost:8080/h2-console/ 」でコンソールを参照できます。

スクリーンショット 2018-12-19 20.08.08.png

スクリーンショット 2018-12-19 22.16.17.png

ログインするとSQLを実行できる画面が表示されるので、適当にテーブルとデータを作成、登録してください。

create table test (
  name varchar2(10),
  no int
);

insert into test values ('hoge1', 10);
insert into test values ('hoge2', 20);
insert into test values ('hoge3', 30);

Repository、Service

作成テーブルからデータを取得するRepositoryとServiceを作成します。

User.kt
data class User(val name: String, val no:Int)
DemoRepository.kt
interface DemoRepository {
    fun all(): List<User>
    fun findByName(name: String): User
}
JdbcDemoRepository.kt
import com.example.demo.repository.DemoRepository
import com.example.demo.repository.User
import org.springframework.jdbc.core.RowMapper
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
import org.springframework.stereotype.Repository

@Repository
class JdbcDemoRepository(private val jdbcTemplate: NamedParameterJdbcTemplate) : DemoRepository {

    private val rowMapper = RowMapper<User> { rs, _ ->
        User(
                rs.getString("name"),
                rs.getInt("no")
        )
    }

    override fun all(): List<User> =
        jdbcTemplate.query("select name, no from test", rowMapper)

    override fun findByName(name: String): User {
        val res = jdbcTemplate.query(
                "select name, no from test where name = :targetName",
                MapSqlParameterSource().addValue("targetName", name),
                rowMapper
        )

        return res.requireNoNulls()[0]
    }
}
DemoService.kt
import com.example.demo.repository.User

interface DemoService {
    fun findUser(name: String): User
}
DemoServiceImpl.kt
import com.example.demo.repository.DemoRepository
import com.example.demo.repository.User
import com.example.demo.service.DemoService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class DemoServiceImpl(@Autowired var repository: DemoRepository): DemoService {
    override fun findUser(name: String): User = repository.findByName(name)
}

最初に作成したControllerを修正します。

DemoRestController.kt
import com.example.demo.service.DemoService
import com.fasterxml.jackson.annotation.JsonProperty
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.validation.Valid
import javax.validation.constraints.NotBlank

@Validated
@RestController
@RequestMapping(value = ["/api"])
class DemoRestController {

    @Autowired
    lateinit var service: DemoService

    @GetMapping(
            value = ["/{name}"],
            produces = [MediaType.APPLICATION_JSON_VALUE]
            )
    fun index(@PathVariable("name") @Valid @NotBlank userName: String): IndexJson {
        val user= service.findUser(userName)
        return IndexJson(user.name, user.no)
    }
}

data class IndexJson(
        @JsonProperty(value = "name") val name: String,
        @JsonProperty(value = "no") val no: Int
)

再起動してAPIを呼んでみます。

$ curl http://localhost:8080/api/hoge1
{"name":"hoge1","no":10}
$ curl http://localhost:8080/api/hoge2
{"name":"hoge2","no":20}

所感

ハマり所は、ほぼありませんでした。
SpringといえばDIですが(?)、プロパティ宣言部分をKotlinの場合は、どう書くかで悩んだのと、
コンストラクタの書き方が慣れなかったくらいでした。

これからもKotlinを使っていこうと思いが強くなりました。
Let's Enjoy Kotlin!!

9
14
0

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
9
14