Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
KotlinでSpring 完全理解ガイド #jsug
Search
Taro Nagasawa
January 27, 2020
Programming
6
3.5k
KotlinでSpring 完全理解ガイド #jsug
日本Springユーザグループの勉強会、登壇資料
https://jsug.doorkeeper.jp/events/102390
Java x Springユーザ向け資料です
Taro Nagasawa
January 27, 2020
Tweet
Share
More Decks by Taro Nagasawa
See All by Taro Nagasawa
Android開発者のための Kotlin Multiplatform入門
ntaro
0
760
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.3k
#Ubie 狂気の認知施策と選考設計
ntaro
13
13k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.1k
Kotlinでサーバサイドを始めよう!
ntaro
1
1k
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
2.8k
Kotlin Contracts #m3kt
ntaro
4
4.2k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
510
Kotlin Fest 2018 - Opening session
ntaro
0
4.3k
Other Decks in Programming
See All in Programming
Rancher と Terraform
fufuhu
2
200
AIでLINEスタンプを作ってみた
eycjur
1
220
Flutter with Dart MCP: All You Need - 박제창 2025 I/O Extended Busan
itsmedreamwalker
0
120
Microsoft Orleans, Daprのアクターモデルを使い効率的に開発、デプロイを行うためのSekibanの試行錯誤 / Sekiban: Exploring Efficient Development and Deployment with Microsoft Orleans and Dapr Actor Models
tomohisa
0
230
Protocol Buffersの型を超えて拡張性を得る / Beyond Protocol Buffers Types Achieving Extensibility
linyows
0
110
AIエージェント開発、DevOps and LLMOps
ymd65536
1
380
Updates on MLS on Ruby (and maybe more)
sylph01
1
180
MCPで実現するAIエージェント駆動のNext.jsアプリデバッグ手法
nyatinte
7
1k
速いWebフレームワークを作る
yusukebe
5
1.6k
さようなら Date。 ようこそTemporal! 3年間先行利用して得られた知見の共有
8beeeaaat
2
1.2k
MCPでVibe Working。そして、結局はContext Eng(略)/ Working with Vibe on MCP And Context Eng
rkaga
5
1.7k
Ruby×iOSアプリ開発 ~共に歩んだエコシステムの物語~
temoki
0
210
Featured
See All Featured
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
Rails Girls Zürich Keynote
gr2m
95
14k
jQuery: Nuts, Bolts and Bling
dougneiner
64
7.9k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
Rebuilding a faster, lazier Slack
samanthasiow
83
9.1k
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Building an army of robots
kneath
306
46k
Bash Introduction
62gerente
615
210k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Building Better People: How to give real-time feedback that sticks.
wjessup
368
19k
Making the Leap to Tech Lead
cromwellryan
134
9.5k
Transcript
2020-01-27 長澤 太郎 KotlinでSpring 完全理解ガイド
本発表のゴール チョットデキル 何もわからない 完全に理解した ここまで ガイドします!
より具体的には 「Kotlinでも普通にSpring使えるんだ!」 「Kotlinにはこういう事情があるんだ!」 が わかるようになる!
長澤 太郎
◦◦を使えば Kotlinで簡単に Spring開発を始められる
Spring Initializr - start.spring.io
Spring Initializr - start.spring.io
IntelliJ IDEA
すぐに開発を始められる!
◦◦を使えば Kotlinで簡単に Spring開発を始められる 答. Spring Initializr
おまけ ビルドツールにGradleを選択すると ビルド設定ファイルがbuild.gradle.kts として生成され、そのスクリプトが Kotlinで記述されている
Kotlinでも◦◦を使って bean登録やハンドラ定義
Hello World @Service class HelloWorldService { fun helloWorld(): String =
"Hello, world!" } @RestController class HelloWorldController(val helloWorldService: HelloWorldService) { @GetMapping("/hello-world") fun helloWorld(): String = helloWorldService.helloWorld() }
Hello World @Service class HelloWorldService { fun helloWorld(): String =
"Hello, world!" } @RestController class HelloWorldController(val helloWorldService: HelloWorldService) { @GetMapping("/hello-world") fun helloWorld(): String = helloWorldService.helloWorld() }
Kotlin事情: デフォルトで継承を許可しない • Kotlinのクラスはデフォルトで継承を許可しない • open修飾子を付けることで継承を許可する • @Serviceなどが付いたクラスはSpringによってサブクラス が生成される(継承を許可する必要がある) @Service
open class FooService { ... } @Service open class BarService { ... } 面倒だしダサい
大丈夫!基本的に意識する必要なし! • Kotlin公式 allopenプラグイン ◦ 自分で指定したアノテーションが付与されたクラスをすべてopenクラス として扱ってくれるプラグイン • kotlin-springプラグイン ◦
Spring用allopenプラグイン ◦ 予め@Serviceや@Configurationのようなアノテーションが allopen対象として登録されている Spring Initializrで生成したプロジェクトには 最初から設定されているので意識する必要はない
Kotlin事情: バリデーションに注意 class PostBody( @NotNull val value: Int ) @PostMapping
fun create( @Valid @RequestBody body: PostBody, bindingResult: BindingResult ) { ... }
Kotlin事情: バリデーションに注意 class PostBody( @NotNull val value: Int ) @PostMapping
fun create( @Valid @RequestBody body: PostBody, bindingResult: BindingResult ) { ... } こっちは問題なし
Kotlin事情: バリデーションに注意 class PostBody( @NotNull val value: Int ) @PostMapping
fun create( @Valid @RequestBody body: PostBody, bindingResult: BindingResult ) { ... } プロパティはJavaで言う フィールドとアクセサが組み合 わさったようなもの NotNull型 + Javaのプリミティ ブ型 = ...!?
Kotlin事情: バリデーションに注意 class PostBody( @NotNull val value: Int ) class
PostBody( @field:NotNull val value: Int? )
Kotlinでも◦◦を使って bean登録やハンドラ定義 答. アノテーション
おまけ もしフィールドインジェクションがしたいなら... @RestController class HelloWorldController { @Autowired lateinit var helloWorldService:
HelloWorldService @GetMapping("/hello-world") fun helloWorld(): String { ... } }
◦◦を使った比較的新しい bean登録やハンドラ定義
Bean Definition DSL fun main(args: Array<String>) { SpringApplicationBuilder() .sources(DemoApplication::class.java) .initializers(beans
{ bean<UserRepositoryImpl>() bean<UserService>() }) .run(*args) }
[NEW] Router DSL bean { val userService = ref<UserService>() router
{ "/api".nest { GET("/users") { val users = userService.findAll() ok().body(users) } } } }
[NEW] Router DSL val userHandler = ref<UserHandler>() router { "/api".nest
{ GET("/users", userHandler::findAll) } } class UserHandler(val userService: UserService) { fun findAll(req: ServerRequest): ServerResponse { val users = userService.findAll() return ServerResponse.ok().body(users) } }
◦◦を使った比較的新しい bean登録やハンドラ定義 答. Kotlin用 DSL
おまけ • ktlint - いわゆる Linter 兼 Formatter • Gradleプラグインがある
• IntelliJ IDEAのフォーマッタの自動設定あり $ ./gradlew ktlintCheck $ ./gradlew ktlintFormat
WebFluxでKotlinの ◦◦という機能が便利
Kotlinでも普通にWebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():
Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } }
Kotlinでも普通にWebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():
Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } AとBを同時に取得して ペアとしてまとめる
Kotlinでも普通にWebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():
Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } AとBを同時に取得して ペアとしてまとめる CをMonoとして取得 Monoの入れ子を解除
Kotlinでも普通にWebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():
Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } AとBを同時に取得して ペアとしてまとめる CをMonoとして取得 Monoの入れ子を解除 Cの結果のプロパティで変換
Kotlinでも普通にWebFlux @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle():
Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } } AとBを同時に取得して ペアとしてまとめる CをMonoとして取得 Monoの入れ子を解除 Cの結果のプロパティで変換
Kotlinにはコルーチンがある! • Reactor対応のライブラリが公式である ◦ コルーチンでMonoやFluxを表現できる • というかむしろSpring Framework 5.2から◦◦! •
詳しくは次の木原さんの発表で!
WebFluxでKotlinの ◦◦という機能が便利 答. コルーチン
おまけ • Ktor というKotlin用Webアプリフレームワークがいい 感じ • JetBrains公式 • 非常に薄く、余分な機能は3rdパーティ任せ ◦
ロギング、永続化、テンプレートエンジン、DI • DSL ◦ ラムダ(特に拡張関数としてのラムダ)を多様 ◦ 宣言的にプログラムを組み立てる • ノンブロッキング ◦ 複雑な非同期プログラミングをコルーチンで
Kotlinでも◦◦を使ったテスト
class FooTest { @Nested inner class fooMethod { @Test fun
`should throw exception`() { assertThrows<MyException>() { Foo().foo() } } } } JUnit5
class FooTest { @Nested inner class fooMethod { @Test fun
`should throw exception`() { assertThrows<MyException>() { Foo().foo() } } } } JUnit5 グルーピングしてテストの見通しを良く
class FooTest { @Nested inner class fooMethod { @Test fun
`should throw exception`() { assertThrows<MyException>() { Foo().foo() } } } } JUnit5
client.get() .uri("/hello-world") .exchange() .expectBody(HelloWorldResource::class.java) .isEqualTo<Nothing>(expectedResource) WebTestClientによるテスト Javaと同じ感覚で 書いてるとこうなりそう
client.get() .uri("/hello-world") .exchange() .expectBody(HelloWorldResource::class.java) .isEqualTo<Nothing>(expectedResource) WebTestClientによるテスト Javaと同じ感覚で 書いてるとこうなりそう ここで例外が発生する!!
client.get() .uri("/hello-world") .exchange() .expectBody<HelloWorldResource>() .isEqualTo(expectedResource) Kotlin用の拡張関数 expectBodyを使う
Kotlinでも◦◦を使ったテスト 答. JUnit
おまけ • アサーションライブラリは何がいいか • 弊社では AssertJを使っています • assertkが気になる ◦ Kotlinフレンドリ(nullまわりとか)
◦ ただし v0.21 で不安定か? assertThat(yourName).isEqualTo("Alice")
Kotlinでモックするなら ◦◦がイイ感じ
MockK val userRepo = mockk<UserRepository>() every { userRepo.findUser(1) } returns
user 通常のメソッドであれば このように挙動を変更できる interface UserRepository { suspend fun findUser(id: Long): User? }
MockK interface UserRepository { suspend fun findUser(id: Long): User? }
val userRepo = mockk<UserRepository>() every { userRepo.findUser(1) } returns user coEvery { userRepo.findUser(1) } returns user 今回はコルーチン(suspend関数)なので
モック生成を繰り返さないこと class DesignControllerTest { private lateinit var repo: DesignRepository private
lateinit var client: DesignClient private lateinit var controller: DesignController @BeforeEach fun init() { repo = mockk() client = mockk() controller = DesignController(repo, client) } } 高コスト 参考 https://www.youtube.com/watch?v=RX_g65J14H0
モック生成は一度、都度リセット class DesignControllerTest { private val repo: DesignRepository = mockk()
private val client: DesignClient = mockk() private val controller: DesignController(repo, client) @BeforeEach fun init() { clearMocks(repo, client) } } 参考 https://www.youtube.com/watch?v=RX_g65J14H0
そのほか基本的なことは一通りできる // キャプチャ val slot = slot<String>() // 戻り値がないメソッド every
{ myService.run(capture(slot)) } just runs // 検証 verify(exactly = 1) { myService.run(any()) }
Kotlinでモックするなら ◦◦がイイ感じ 答. Mockk
まとめ • Spring InitializrやIntelliJで簡単にSpring Kotlinを始めら れる! • 普通にアノテーションを使ってもいいし、DSLを使ってもbean 登録やルーティングができる ◦
Kotlin固有の問題はあまりないが、バリデーションに注意 • コルーチンというものが便利っぽいぞ • KotlinでもJUnitでSpringテストができる • モックライブラリはMockKがよさそう
「Spring Kotlin, 完全に理解した!」
よくある質問 「DBアクセスは どうしてる?」