【1日1zenn - day11】Spring Bootをざっくりキャッチアップ
day10に続き、基礎知識の仕入れに使います。
BEのコードを見ていると、Kotlin特有の書き方もあれば、Spring Boot特有の書き方もあり、なんとなく読むのはできたりしますがそのせいで処理を見落として沼ったりしてました。
前回はkotlinのドキュメントをざっくりキャッチアップしたので、今回はSpring Bootをキャッチアップした上で、次回以降は実際に処理をどれくらい早く理解できるか、ボトルネックになっているところは何か特定していきます。
そもそもSpiring Bootが何であって、何ではないのかがわかってないという感じ。
一旦記事を読む
ドキュメントを読もうとしたら概要の把握が少し難しそうだったので、一旦記事でざっくり掴んでいきます。
いつも通り思ったことを自分用に箇条書き。
1記事目
- システムの開発効率・保守性を高める「DI」と「AOP」が特徴らしい
- DIは依存性の注入。クリーンアーキテクチャを一瞬かじったときとかに聞いた
- クラスやインターフェースの依存関係において、普通は片方を変えたらもう片方も修正しなきゃいけないし、特に使う側が複数ある場合は色んな場所を修正する必要がある
- これを、使われる側のクラスに
@Component
と付けて、使う側のクラスに@Autowired
とつけるだけで、DIコンテナにインスタンスを作って、使う側は自動で修正されるらしい- 要はnew hoge()とかしなくて良くなるから、newの方も直さなきゃ的な二度手間がないイメージかな。
- 生成されたインスタンスはBeanといって、コードで見たことあるな。他リポジトリのAPIとの繋ぎ込みでWebClientインスタンスみたいなのを作ってた気がする。
- どう紐付ける?
-
https://www.shookuro.com/entry/2016/08/09/175801
- Springがコンテナの中からそのプロパティの型に合うクラスを見つけて、インスタンスを裏でnewしてくれるとのこと
- 確かにコードで見たのも型でインスタンス名っぽいのを指定してたから、納得感ある
-
https://www.shookuro.com/entry/2016/08/09/175801
- AOPは、共通する処理を抜き出してまとめて管理する仕組みらしい
- Commonみたいなのをまとめるイメージかな。
- こっちは実務で使ってる場所なさそうだったから一旦ペンド
- DIは依存性の注入。クリーンアーキテクチャを一瞬かじったときとかに聞いた
2記事目
よく使うアノテーションを軸に知っていきたい。
- @Autowired
- Beanを使う側につけることで、使っているクラスが変わっても修正しなくていいようにする
- 実務だとコンストラクタで使われたりしてる
- @Configrationの中の@Beanでインスタンス化して使ってたり。
-
https://qiita.com/bluespoon/items/6060389eab983c2e045e
- 注入するのは、コンストラクタの前(コンストラクタインジェクション)と、セッターメソッドの前(セッターインジェクション)と、変数の前(フィールドインジェクション)らしい
- コンストラクタインジェクションがおすすめで、しかもアノテーションは省略しても良くなってるらしい
- まだ腹落ちはしてない(便利さを体感してない)が、まあ一旦先に進む
- @Configuration
- DIコンテナへのBean登録が複雑な場合に、JavaConfigというクラスに付与することでBeanを作れるらしい
- @Bean
- @Configurationが付与されたJavaConfig内のインスタンスを生成するメソッドに付与するとのこと
- 複雑なときはConfigrationを付けて、newする代わりにBeanをつけるという
- @RestController
- そのクラスのメソッドの戻り値がHTTPのレスポンスボディで返されるらしい
- これそんな機能があったんか。よく見る。
- そのクラスのメソッドの戻り値がHTTPのレスポンスボディで返されるらしい
- @RequestMapping
- 実務だと
@RequestMapping("/api/cart")
みたいに使ってる - 呼び出しに対してこのクラスが実行しますよと定義する感じ
-
@GetMapping
も同じ感じ
- 実務だと
- @RequestParam
- これもよく見る
- URLに含まれるクエリパラメータや、リクエストボディに含まれるパラメータを受け取るために使用するアノテーションらしい
- Reactでいうquery.getとかURLSearchParamsとかと同じイメージかな
- パラメータを必須にしないときに
require=false
と指定する - nameやvalueを指定する
- パラメータが指定されなかったときのdefaultValueも指定できるらしい
- URLに含まれるクエリパラメータや、リクエストボディに含まれるパラメータを受け取るために使用するアノテーションらしい
- これもよく見る
他に実務で出てくるアノテーションめも
- @Value
-
https://spring.pleiades.io/spring-framework/reference/core/beans/annotation-config/value-annotations.html
- 外部化されたプロパティを注入するために使用されるらしい
-
@Value("\${catalog.name}")
みたいに書くといいっぽいんだけど、ドキュメントわかりにくいので調べる -
https://qiita.com/kazuki43zoo/items/0ce92fce6d6f3b7bf8eb
- データベースへの接続情報などの環境に依存する値など、ソースコード上に書きたくない値を使う手段として@Valueがあったりする
- 実務でも他リポジトリのAPIのURLを@Valueで書いたりしてる
- applivation.yamlに書かれているものを引っ張ってきたり。
- DIコンテナで管理するクラスのフィールド、setterメソッド、コンストラクタ引数などに
@Value(${設定名:デフォルト値})
の形式で指定すると、指定した設定の値が注入されるらしい
- データベースへの接続情報などの環境に依存する値など、ソースコード上に書きたくない値を使う手段として@Valueがあったりする
-
https://spring.pleiades.io/spring-framework/reference/core/beans/annotation-config/value-annotations.html
@Value("${app.timezone:UTC}")
private TimeZone timezone; // ← "app.timezone"の設定値がインジェクションされる
- timezone型のTimeZoneに、 app.timezoneで書かれてる内容が入ってくる感じか
- カスタムアノテーション
-
https://qiita.com/wrongwrong/items/8abf3b1b09654da3cb36
-
annotation class
と宣言した後でdataクラスと同じ感じで書けばバリデーションできるっぽい
-
-
https://qiita.com/wrongwrong/items/8abf3b1b09654da3cb36
kotlinとSpringの記事を読む
- これが結構読みたかったやつかも。感じたメリット
- 同じ処理をするときにコード量を2-3割減らせる
- 確かに依存注入とか、@Valueとか、使わないと記述量増えそう
- 2-3割も変わるかな?とは思うが、これはKotlinの力な感じか。
- Kotlinのdata class を使うとtoStringとかサクッと作れてLombokが不要になる
- Javaで定義したことないけど確かに便利そう
- 文字列に変数を埋め込みやすい
- これは確かに便利に感じてる。
- null安全にできる
- これはTypeScriptもそうだからあまり革新性を感じてない
- コレクション操作が充実してる
- listOfとか
- ここいらへん、読んでみたが、JSと同じテンションで色々使えそう
- フロントではmap系とか頻繁に使うイメージだが、BEでの使い所がまだ掴めてない
- 記憶においておく
https://qiita.com/okaponta_/items/d2d8c5af2e4c611d0ed3
- listOfとか
- 同じ処理をするときにコード量を2-3割減らせる
- テストしやすさは確かにありそうだけどまだそれほど体感してない
- application.yamlから設定値を引っ張って来れるのは感じた
- マイクロサービスアーキテクチャだと特に楽みたいな話あるんかな
-
https://licensecounter.jp/devops-hub/blog/spring-boot1/
- 『「あるURLにアクセスが来たら、それに結び付けられた処理を呼び出す」という処理をシンプルに書ける』とあって、
@GetMapping
とかがそれにあたるのかな。 - コンパイルとかデプロイが軽量らしくて、小さく分割するなら合ってるみたいな話もありそう?
https://zenn.dev/tm35/articles/05e74b6dd9f831
- 『「あるURLにアクセスが来たら、それに結び付けられた処理を呼び出す」という処理をシンプルに書ける』とあって、
-
https://licensecounter.jp/devops-hub/blog/spring-boot1/
- マイクロサービスアーキテクチャだと特に楽みたいな話あるんかな
- @Configurationは、Springの色々な設定をJavaコード上で行うためのアノテーションらしい。そうなんだ。
実務のコードを読んで気になったのを調べる
companion object
これはkotlin
- 1クラス内に1つだけ宣言可能
- クラスに属するSingletonが作成できる
- そもそもSingletonって?
- https://magazine.techacademy.jp/magazine/18939
- 複数のインスタンスが作られると困るときに制限する
- 何かを実行したことでインスタンスが変わったりしないようにするっぽい?
- GPTに聞いた
- Javaのstaticみたいにインスタンス化しないでもアクセスできるメンバーを定義できる
- クラスの初期化のタイミングでcompanion objectとして作っておけば、
クラス名.オブジェクト名
とかで呼び出せる - クラス内のグローバル変数的なテンションで使えそうな雰囲気
- クラスの初期化のタイミングでcompanion objectとして作っておけば、
- Javaのstaticみたいにインスタンス化しないでもアクセスできるメンバーを定義できる
WebSession attributes
これはSpring
ドンピシャなドキュメント見つからなかったけど、WebSession.getAttributesとかでセッションに保存されている属性名を取り出せる @GetMapping("/setSessionData")
public String setSessionData(HttpSession session) {
// セッションにデータを保存
session.setAttribute("key", "value");
return "Data set in session";
}
```とかでセットできる
@GetMapping("/getSessionData")
public String getSessionData(HttpSession session) {
// セッションからデータを取得
String value = (String) session.getAttribute("key");
return "Data from session: " + value;
}
```
とかでゲットできる。
suspend
非同期処理が含まれる場合はsuspendで指定しないとエラーになる
runCatching
以下みたいに書ける
fun main() {
val result = runCatching {
// エラーが発生する可能性のある処理
10 / 2
}
result.onSuccess {
println("成功時の結果: $it")
}.onFailure {
println("エラー発生: ${it.message}")
}
}
結果はResult型で取得されて、onSuccessとかonFailureとかで簡単に分岐させられる。
fold
kotlin。runCatchingとかと一緒に出てくる
要は成功した場合と失敗した場合の処理を一つのブロックで書けるとか。
ドキュメントのサンプルは以下。
val fruits = listOf("cherry", "blueberry", "citrus", "apple", "apricot", "banana", "coconut")
val evenFruits = fruits.groupingBy { it.first() }
.fold({ key, _ -> key to mutableListOf<String>() },
{ _, accumulator, element ->
accumulator.also { (_, list) -> if (element.length % 2 == 0) list.add(element) }
})
val sorted = evenFruits.values.sortedBy { it.first }
println(sorted) // [(a, []), (b, [banana]), (c, [cherry, citrus])]
これをfold使わないと以下とかになるっぽい
val fruits = listOf("cherry", "blueberry", "citrus", "apple", "apricot", "banana", "coconut")
// グループ化して初期化
val grouped = mutableMapOf<Char, Pair<Char, MutableList<String>>>()
for (fruit in fruits) {
val key = fruit.first()
grouped.putIfAbsent(key, key to mutableListOf())
}
// 条件に応じてリストを更新
for (fruit in fruits) {
if (fruit.length % 2 == 0) {
val key = fruit.first()
val (_, list) = grouped[key] ?: continue
list.add(fruit)
}
}
// ソート
val sorted = grouped.values.sortedBy { it.first }
println(sorted) // [(a, []), (b, [banana]), (c, [cherry, citrus])]
初期値の生成と更新処理を一気に行うことができるのは確かに便利だ。
けどエラーハンドリングの場合は初期値とかじゃない雰囲気だから、もうちょい知る必要ありそう。
LinkedMultiValueMap
queryParamsを作るときによく見る。
GPTに聞いたら、順番を保持できたり、各キーに対してListっぽく複数の値を格納したり、.addとか簡単に行ったりできるらしい。
確かに色々書いてある。一旦addしてパラメ作るのを今見てるけど、それ以外にも色々ありそう
おわりに
なんか今日やったことが合っていたのかわからん
とりあえず色々調べたという感じで、無限の沼にただ沈んでいった気もする。。
眠いので寝ます。
Discussion