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.3k
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
260
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.1k
#Ubie 狂気の認知施策と選考設計
ntaro
13
12k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1k
Kotlinでサーバサイドを始めよう!
ntaro
1
900
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
2.5k
Kotlin Contracts #m3kt
ntaro
4
3.7k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
420
Kotlin Fest 2018 - Opening session
ntaro
0
4.2k
Other Decks in Programming
See All in Programming
Pythonで改めて考える「クラス(class)」の使いどころ
os1ma
4
900
What you can do with Ruby on WebAssembly
kateinoigakukun
0
170
あなたのアプリ、ログはでてますか?あるいはログをだしてますか? (Funabashi.dev用 軽量版)
uzulla
2
130
Developer Joy == Developer Productivity (really!)
hollycummins
1
220
Prompt Cachingは本当に効果的なのか検証してみた.pdf
ttnyt8701
0
530
LangChainの現在とv0.3にむけて
os1ma
4
940
事業フェーズの変化に対応する 開発生産性向上のゼロイチ
masaygggg
0
210
GraphQLの魅力を引き出すAndroidクライアント実装
morux2
3
800
Scala におけるコンパイラエラーとの付き合い方
chencmd
2
430
Google Sign-inの移行から始めるCredential Manager活用
clockvoid
0
380
オートマトン学習しろ / Do automata learning
makenowjust
3
130
Rubyとクリエイティブコーディングの輪の広がり / The Growing Circle of Ruby and Creative Coding
chobishiba
1
270
Featured
See All Featured
Documentation Writing (for coders)
carmenintech
65
4.3k
The Art of Programming - Codeland 2020
erikaheidi
48
13k
Statistics for Hackers
jakevdp
794
220k
The Illustrated Children's Guide to Kubernetes
chrisshort
47
48k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
23
1.7k
Keith and Marios Guide to Fast Websites
keithpitt
408
22k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
36
1.7k
Thoughts on Productivity
jonyablonski
66
4.2k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
0
130
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.3k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
5
490
What's new in Ruby 2.0
geeforr
340
31k
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アクセスは どうしてる?」