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
780
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
520
Kotlin Fest 2018 - Opening session
ntaro
0
4.3k
Other Decks in Programming
See All in Programming
Reduxモダナイズ 〜コードのモダン化を通して、将来のライブラリ移行に備える〜
pvcresin
2
680
Advance Your Career with Open Source
ivargrimstad
0
330
AIで開発生産性を上げる個人とチームの取り組み
taniigo
0
130
Conquering Massive Traffic Spikes in Ruby Applications with Pitchfork
riseshia
0
150
CSC509 Lecture 01
javiergs
PRO
1
430
Web技術を最大限活用してRAW画像を現像する / Developing RAW Images on the Web
ssssota
2
1.2k
iOSアプリの信頼性を向上させる取り組み/ios-app-improve-reliability
shino8rayu9
0
150
なぜあの開発者はDevRelに伴走し続けるのか / Why Does That Developer Keep Running Alongside DevRel?
nrslib
3
370
株式会社 Sun terras カンパニーデック
sunterras
0
220
CSC305 Lecture 01
javiergs
PRO
1
400
いま中途半端なSwift 6対応をするより、Default ActorやApproachable Concurrencyを有効にしてからでいいんじゃない?
yimajo
2
340
ポスターセッション: 「まっすぐ行って、右!」って言ってラズパイカーを動かしたい 〜生成AI × Raspberry Pi Pico × Gradioの試作メモ〜
komofr
0
950
Featured
See All Featured
Product Roadmaps are Hard
iamctodd
PRO
54
11k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.2k
GraphQLの誤解/rethinking-graphql
sonatard
73
11k
How to train your dragon (web standard)
notwaldorf
96
6.3k
The World Runs on Bad Software
bkeepers
PRO
71
11k
The Power of CSS Pseudo Elements
geoffreycrofte
79
6k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Agile that works and the tools we love
rasmusluckow
331
21k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
132
19k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
14k
Code Reviewing Like a Champion
maltzj
525
40k
jQuery: Nuts, Bolts and Bling
dougneiner
64
7.9k
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アクセスは どうしてる?」