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.4k
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
640
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.2k
#Ubie 狂気の認知施策と選考設計
ntaro
13
13k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.1k
Kotlinでサーバサイドを始めよう!
ntaro
1
970
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
2.7k
Kotlin Contracts #m3kt
ntaro
4
4k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
490
Kotlin Fest 2018 - Opening session
ntaro
0
4.3k
Other Decks in Programming
See All in Programming
The Missing Link in Angular’s Signal Story: Resource API and httpResource
manfredsteyer
PRO
0
140
eBPF超入門「o11yに使える」とは (20250424_eBPF_o11y)
thousanda
1
120
KANNA Android の技術的課題と取り組み
watabee
1
480
知識0からカンファレンスやってみたらこうなった!
syossan27
4
220
ComposeでのPicture in Picture
takathemax
0
140
カオスに立ち向かう小規模チームの装備の選択〜フルスタックTSという装備の強み _ 弱み〜/Choosing equipment for a small team facing chaos ~ Strengths and weaknesses of full-stack TS~
bitkey
1
140
SwiftDataのカスタムデータストアを試してみた
1mash0
0
150
Instrumentsを使用した アプリのパフォーマンス向上方法
hinakko
0
250
開発者フレンドリーで顧客も満足?Platformの秘密
algoartis
0
210
Beyond_the_Prompt__Evaluating__Testing__and_Securing_LLM_Applications.pdf
meteatamel
0
110
カウシェで Four Keys の改善を試みた理由
ike002jp
1
140
エンジニア向けCursor勉強会 @ SmartHR
yukisnow1823
3
12k
Featured
See All Featured
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
GitHub's CSS Performance
jonrohan
1031
460k
Why You Should Never Use an ORM
jnunemaker
PRO
56
9.4k
Rebuilding a faster, lazier Slack
samanthasiow
81
9k
4 Signs Your Business is Dying
shpigford
183
22k
How GitHub (no longer) Works
holman
314
140k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Documentation Writing (for coders)
carmenintech
71
4.8k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Building a Modern Day E-commerce SEO Strategy
aleyda
40
7.2k
A better future with KSS
kneath
239
17k
Into the Great Unknown - MozCon
thekraken
38
1.8k
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アクセスは どうしてる?」