带注解的定义
Koin 注解允许声明与常规 Koin DSL 相同类型的定义,但通过注解完成。只需为你的类添加所需的注解标签,它就会为你生成所有内容!
例如,等同于 single { MyComponent(get()) } DSL 声明,只需像这样添加 @Single 标签即可:
@Single
class MyComponent(val myDependency : MyDependency)Koin 注解与 Koin DSL 保持相同的语义。你可以使用以下定义声明你的组件:
@Single- 单例实例(在 DSL 中使用single { }声明)@Factory- 工厂实例。每次需要实例时都会重新创建的实例。(在 DSL 中使用factory { }声明)@KoinViewModel- Android ViewModel 实例(在 DSL 中使用viewModel { }声明)@KoinWorker- Android Worker Workmanager 实例(在 DSL 中使用worker { }声明)
关于作用域,请查看 声明作用域 小节。
为 Kotlin Multiplatform 生成 Compose ViewModel (从 1.4.0 版本开始)
默认情况下,@KoinViewModel 注解使用 koin-core-viewmodel 主 DSL 生成 ViewModel(自 2.2.0 版起启用)。这提供了 Kotlin Multiplatform 兼容性并使用统一的 ViewModel API。
KOIN_USE_COMPOSE_VIEWMODEL 选项默认启用:
ksp {
// This is the default behavior since 2.2.0
arg("KOIN_USE_COMPOSE_VIEWMODEL","true")
}这将使用 org.koin.compose.viewmodel.dsl.viewModel 生成 viewModel 定义,以实现多平台兼容性。
INFO
KOIN_USE_COMPOSE_VIEWMODEL自 Annotations 2.2.0 起默认启用- 这确保了与所有平台统一的 ViewModel API 的一致性
- 旧的
USE_COMPOSE_VIEWMODEL键已被移除
自动绑定或特定绑定
声明组件时,所有检测到的“绑定”(关联的超类型)都将为你准备好。例如,以下定义:
@Single
class MyComponent(val myDependency : MyDependency) : MyInterfaceKoin 将声明你的 MyComponent 组件也绑定到 MyInterface。等同的 DSL 是 single { MyComponent(get()) } bind MyInterface::class。
除了让 Koin 自动检测外,你还可以使用 binds 注解参数指定你真正想要绑定的类型:
@Single(binds = [MyBoundType::class])可空依赖
如果你的组件使用了可空依赖,不用担心,它将自动为你处理。继续使用你的定义注解,Koin 将推断该怎么做:
@Single
class MyComponent(val myDependency : MyDependency?)生成的 DSL 等价物将是 single { MyComponent(getOrNull()) }
请注意,这也适用于注入的参数和属性。
使用 @Named 的限定符
你可以为定义添加一个“名称”(也称为限定符),以使用 @Named 注解区分同一类型的多个定义:
@Single
@Named("InMemoryLogger")
class LoggerInMemoryDataSource : LoggerDataSource
@Single
@Named("DatabaseLogger")
class LoggerLocalDataSource(private val logDao: LogDao) : LoggerDataSource解决依赖时,只需使用 named 函数配合限定符即可:
val logger: LoggerDataSource by inject(named("InMemoryLogger"))也可以创建自定义限定符注解。使用之前的示例:
@Named
annotation class InMemoryLogger
@Named
annotation class DatabaseLogger
@Single
@InMemoryLogger
class LoggerInMemoryDataSource : LoggerDataSource
@Single
@DatabaseLogger
class LoggerLocalDataSource(private val logDao: LogDao) : LoggerDataSourceval logger: LoggerDataSource by inject(named<InMemoryLogger>())使用 @InjectedParam 注入参数
你可以将构造函数成员标记为“注入参数”,这意味着在调用解析时,依赖项将被传递到图 (graph) 中。
例如:
@Single
class MyComponent(@InjectedParam val myDependency : MyDependency)然后你可以调用你的 MyComponent 并传入 MyDependency 的实例:
val m = MyDependency()
// Resolve MyComponent while passing MyDependency
koin.get<MyComponent> { parametersOf(m) }生成的 DSL 等价物将是 single { params -> MyComponent(params.get()) }
注入懒加载依赖 - Lazy<T>
Koin 可以自动检测并解析懒加载依赖。例如,在这里我们想要懒加载地解析 LoggerDataSource 定义。你只需按如下方式使用 Kotlin Lazy 类型即可:
@Single
class LoggerInMemoryDataSource : LoggerDataSource
@Single
class LoggerAggregator(val lazyLogger : Lazy<LoggerDataSource>)在底层,它会生成像使用 inject() 而不是 get() 的 DSL:
single { LoggerAggregator(inject()) }注入依赖列表 - List<T>
Koin 可以自动检测并解析所有依赖列表。例如,在这里我们想要解析所有 LoggerDataSource 定义。你只需按如下方式使用 Kotlin List 类型即可:
@Single
@Named("InMemoryLogger")
class LoggerInMemoryDataSource : LoggerDataSource
@Single
@Named("DatabaseLogger")
class LoggerLocalDataSource(private val logDao: LogDao) : LoggerDataSource
@Single
class LoggerAggregator(val datasource : List<LoggerDataSource>)在底层,它会生成像使用 getAll() 函数的 DSL:
single { LoggerAggregator(getAll()) }使用 @Property 的属性
要在你的定义中解析 Koin 属性,只需将构造函数成员标记为 @Property。这将通过传递给注解的值来解析 Koin 属性:
@Factory
public class ComponentWithProps(
@Property("id") public val id : String
)生成的 DSL 等价物将是 factory { ComponentWithProps(getProperty("id")) }
@PropertyValue - 具有默认值的属性 (从 1.4 版本开始)
Koin 注解让你能够直接从你的代码中通过 @PropertyValue 注解为属性定义默认值。 让我们来看这个示例:
@Factory
public class ComponentWithProps(
@Property("id") public val id : String
){
public companion object {
@PropertyValue("id")
public const val DEFAULT_ID : String = "_empty_id"
}
}生成的 DSL 等价物将是 factory { ComponentWithProps(getProperty("id", ComponentWithProps.DEFAULT_ID)) }
JSR-330 兼容性注解
Koin 注解通过 koin-jsr330 模块提供了 JSR-330 (Jakarta Inject) 兼容的注解。这些注解对于从 Hilt、Dagger 或 Guice 等其他 JSR-330 兼容框架迁移的开发者来说尤其有用。
设置
将 koin-jsr330 依赖项添加到你的项目:
dependencies {
implementation "io.insert-koin:koin-jsr330:$koin_version"
}可用的 JSR-330 注解
@Singleton (jakarta.inject.Singleton)
JSR-330 标准单例注解,等同于 Koin 的 @Single:
import jakarta.inject.Singleton
@Singleton
class DatabaseService这会生成与 @Single 相同的结果——Koin 中的一个单例实例。
@Named (jakarta.inject.Named)
JSR-330 标准的限定符注解,用于基于字符串的限定符:
import jakarta.inject.Named
import jakarta.inject.Singleton
@Singleton
@Named("inMemory")
class InMemoryCache : Cache
@Singleton
@Named("redis")
class RedisCache : Cache@Inject (jakarta.inject.Inject)
JSR-330 标准注入注解。虽然 Koin 注解不需要显式标记构造函数,但 @Inject 可用于 JSR-330 兼容性:
import jakarta.inject.Inject
import jakarta.inject.Singleton
@Singleton
class UserService @Inject constructor(
private val repository: UserRepository
)@Qualifier (jakarta.inject.Qualifier)
用于创建自定义限定符注解的元注解:
import jakarta.inject.Qualifier
@Qualifier
annotation class Database
@Qualifier
annotation class Cache
@Singleton
@Database
class DatabaseConfig
@Singleton
@Cache
class CacheConfig@Scope (jakarta.inject.Scope)
用于创建自定义作用域注解的元注解:
import jakarta.inject.Scope
@Scope
annotation class RequestScoped
// Use with Koin's scope system
@Scope(name = "request")
@RequestScoped
class RequestProcessor混合使用
你可以在同一个项目中自由混合 JSR-330 注解和 Koin 注解:
// JSR-330 style
@Singleton
@Named("primary")
class PrimaryDatabase : Database
// Koin style
@Single
@Named("secondary")
class SecondaryDatabase : Database
// Mixed in same class
@Factory
class DatabaseManager @Inject constructor(
@Named("primary") private val primary: Database,
@Named("secondary") private val secondary: Database
)框架迁移优势
使用 JSR-330 注解为框架迁移提供了多项优势:
- 熟悉的 API:来自 Hilt、Dagger 或 Guice 的开发者可以使用已知的注解
- 渐进式迁移:现有的 JSR-330 注解代码只需少量修改即可工作
- 标准合规性:遵循 JSR-330 确保与依赖注入标准的兼容性
- 团队入职:方便熟悉其他 DI 框架的团队快速上手
INFO
Koin 中的 JSR-330 注解会生成与其 Koin 等价物相同的底层 DSL。JSR-330 和 Koin 注解之间的选择纯粹是风格上的,并基于团队偏好或迁移要求。
