Kotlin とアノテーション
このチュートリアルでは、Kotlin アプリケーションを作成し、アノテーションを使用した Koin の依存性注入(DI)を利用してコンポーネントを取得する方法を学びます。 チュートリアルの所要時間は約 10 分 です。
NOTE
更新 - 2024-11-12
コードの取得
セットアップ
まず、以下のように Koin アノテーションの依存関係が追加されていることを確認してください。
plugins {
id("com.google.devtools.ksp") version kspVersion
}
dependencies {
// Kotlin アプリ用 Koin
implementation("io.insert-koin:koin-core:$koin_version")
// Koin アノテーション
implementation("io.insert-koin:koin-annotations:$koin_annotations_version")
ksp("io.insert-koin:koin-ksp-compiler:$koin_annotations_version")
}アプリケーションの概要
このアプリケーションの目的は、ユーザーのリストを管理し、それを UserApplication クラスで表示することです。
Users -> UserRepository -> UserService -> UserApplication
"User" データ
User のコレクションを管理します。データクラスは以下の通りです。
data class User(val name: String, val email: String)ユーザーリストを管理(ユーザーの追加や名前による検索)するための「リポジトリ」コンポーネントを作成します。以下は UserRepository インターフェースとその実装です。
interface UserRepository {
fun findUserOrNull(name: String): User?
fun addUsers(users: List<User>)
}
@Singleton
class UserRepositoryImpl : UserRepository {
private val _users = arrayListOf<User>()
override fun findUserOrNull(name: String): User? {
return _users.firstOrNull { it.name == name }
}
override fun addUsers(users: List<User>) {
_users.addAll(users)
}
}NOTE
このプロジェクトでは、シングルトンコンポーネントを宣言するために Koin の @Singleton アノテーション(org.koin.core.annotation 由来)を使用しています。
Koin モジュール
@Module アノテーションを使用して Koin モジュールを宣言します。
@Module
@ComponentScan("org.koin.sample")
@Configuration
class AppModule@Module- これを Koin モジュールとして宣言します@ComponentScan("org.koin.sample")- 指定したパッケージからアノテーション付きクラスをスキャンして登録します@Configuration-@KoinApplicationによる自動モジュール検出を有効にします
@Singleton アノテーションを追加してコンポーネントを宣言しましょう。
@Singleton
class UserRepositoryImpl : UserRepository {
// ...
}UserService コンポーネント
ユーザー操作を管理する UserService コンポーネントを作成します。
interface UserService {
fun getUserOrNull(name: String): User?
fun loadUsers()
fun prepareHelloMessage(user: User?): String
}
@Singleton
class UserServiceImpl(
private val userRepository: UserRepository
) : UserService {
override fun getUserOrNull(name: String): User? = userRepository.findUserOrNull(name)
override fun loadUsers() {
userRepository.addUsers(listOf(
User("Alice", "[email protected]"),
User("Bob", "[email protected]"),
User("Charlie", "[email protected]")
))
}
override fun prepareHelloMessage(user: User?): String {
return user?.let { "Hello '${user.name}' (${user.email})! 👋" } ?: "❌ User not found"
}
}
UserRepositoryはUserServiceImplのコンストラクタで参照されています。
UserService を @Singleton アノテーションで宣言します。
UserApplication
UserApplication クラスは、コンストラクタ注入を使用して UserService を受け取ります。
@Singleton
class UserApplication(
private val userService: UserService
) {
init {
userService.loadUsers()
}
fun sayHello(name: String) {
val user = userService.getUserOrNull(name)
val message = userService.prepareHelloMessage(user)
println(message)
}
}INFO
コンストラクタ注入は、依存関係を注入するための推奨される方法です。Koin は UserApplication を作成する際、自動的に UserService を解決して注入します。
Koin アプリケーションオブジェクト
Koin のアノテーションベースの設定のエントリーポイントを示すために、@KoinApplication オブジェクトを作成します。
@KoinApplication
object KoinUserApplication@KoinApplication アノテーションは KSP プロセッサと連携し、このオブジェクトに対して startKoin() 拡張関数を生成します。
Koin の開始
アプリケーションで Koin を開始する必要があります。アプリケーションのメインエントリーポイントで、生成された startKoin() 関数を呼び出すだけです。
fun main() {
KoinUserApplication.startKoin()
val userApplication = KoinPlatform.getKoin().get<UserApplication>()
userApplication.sayHello("Alice")
}主なポイント:
KoinUserApplication.startKoin()- すべてのモジュールを自動的に検出してロードする生成関数です。modules()を手動で呼び出す必要はありません。すべてのアノテーション付きの依存関係はコンパイル時に検出されます。KoinPlatform.getKoin().get<UserApplication>()を使用して、Koin からUserApplicationインスタンスを取得します。
INFO
モジュールに @Configuration を付与し、@KoinApplication アノテーションを使用することで、KSP を介してコンパイル時にすべてのアノテーション付き依存関係を自動的に検出し、ロードします。
アノテーション vs Compiler Plugin DSL
アノテーションベースの設定と Compiler Plugin DSL の比較は以下の通りです。
アノテーションを使用する場合:
@Module
@ComponentScan("org.koin.sample")
@Configuration
class AppModule
@Singleton
class UserApplication(private val userService: UserService)
@Singleton
class UserRepositoryImpl : UserRepository
@Singleton
class UserServiceImpl(private val userRepository: UserRepository) : UserServiceCompiler Plugin DSL (kotlin.md より):
val appModule = module {
single<UserApplication>()
single<UserRepositoryImpl>() bind UserRepository::class
single<UserServiceImpl>() bind UserService::class
}どちらのアプローチも同じ結果をもたらします:
- アノテーション: KSP によるコンパイル時の検証、自動モジュール検出。
- Compiler Plugin DSL: コンパイル時の自動配線(Auto-wiring)、よりクリーンな
single<T>()構文。
