Skip to content

JSR-330 相容性

Koin 透過 Koin 註解與 Kotlin 編譯器外掛程式提供完整的 JSR-330 (Jakarta Inject) 相容性。這使得 Koin 成為從 Hilt、Dagger 或其他 JSR-330 相容架構遷移之團隊的絕佳選擇。

為什麼選擇 JSR-330?

JSR-330 定義了一套 Java/Kotlin 相依性注入的標準註解集:

JSR-330 註解Koin 對等項用途
@Inject建構函式偵測標記可注入的建構函式
@Singleton@Single單一執行個體作用域
@Named@Named基於字串的限定符
@Qualifier@Qualifier自訂限定符註解
@Scope@Scope自訂作用域註解

設定

koin-jsr330 相依性新增到您的專案:

kotlin
dependencies {
    implementation("io.insert-koin:koin-jsr330:$koin_version")
}

基本用法

@Singleton 與 @Inject

您現有的 Hilt/Dagger 類別可與 Koin 搭配使用:

kotlin
import jakarta.inject.Inject
import jakarta.inject.Singleton

@Singleton
class UserRepository @Inject constructor(
    private val api: ApiService,
    private val database: UserDatabase
)

@Singleton
class ApiService @Inject constructor(
    private val httpClient: OkHttpClient
)

@Named 限定符

基於字串的限定符運作方式完全相同:

kotlin
import jakarta.inject.Named
import jakarta.inject.Singleton

@Singleton
@Named("api")
class ApiOkHttpClient @Inject constructor() : OkHttpClient()

@Singleton
@Named("image")
class ImageOkHttpClient @Inject constructor() : OkHttpClient()

@Singleton
class NetworkManager @Inject constructor(
    @Named("api") private val apiClient: OkHttpClient,
    @Named("image") private val imageClient: OkHttpClient
)

自訂 @Qualifier 註解

保留您現有的自訂限定符:

kotlin
import jakarta.inject.Qualifier

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IoDispatcher

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class MainDispatcher

@Singleton
class CoroutineModule {
    @IoDispatcher
    fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

    @MainDispatcher
    fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
}

@Singleton
class DataSyncManager @Inject constructor(
    @IoDispatcher private val ioDispatcher: CoroutineDispatcher
)

Android ViewModel

JSR-330 註解可與 Koin 的 ViewModel 支援搭配使用:

kotlin
import jakarta.inject.Inject

@KoinViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    fun loadUser(id: String) {
        viewModelScope.launch {
            val user = userRepository.getUser(id)
            // ...
        }
    }
}

NOTE

對於 ViewModel,請使用 @KoinViewModel(Koin 註解)。JSR-330 並未定義 ViewModel 註解。

從 Hilt 遷移

遷移前 (Hilt)

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var analytics: Analytics
}

@Singleton
class Analytics @Inject constructor(
    @ApplicationContext private val context: Context
)

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .build()
}

遷移後 (搭配 JSR-330 的 Koin)

kotlin
// Application - 使用 startKoin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApplication)
            modules(appModule)
        }
    }
}

// Activity - 使用 by inject()
class MainActivity : AppCompatActivity() {
    private val analytics: Analytics by inject()
}

// 保持 @Singleton 和 @Inject 不變!
@Singleton
class Analytics @Inject constructor(
    private val context: Context  // Koin 會自動提供 Context
)

// 使用 @Single 提供執行個體的模組
@Module
@ComponentScan
class NetworkModule {
    @Single
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .build()
}

混合用法

您可以在同一個專案中自由混用 JSR-330 與 Koin 註解:

kotlin
// JSR-330 風格
@Singleton
@Named("local")
class LocalDatabase @Inject constructor() : Database

// Koin 風格
@Single
@Named("remote")
class RemoteDatabase(private val api: ApiService) : Database

// 混合 - 帶有 JSR-330 注入的 Koin 註解
@Factory
class DatabaseManager @Inject constructor(
    @Named("local") private val local: Database,
    @Named("remote") private val remote: Database
)

編譯器外掛程式支援

Koin 編譯器外掛程式也能辨識 JSR-330 註解:

kotlin
// 在編譯器外掛程式中,這些是等價的
@Singleton
class UserRepository @Inject constructor(private val api: ApiService)

// 編譯器外掛程式 DSL(產生相同的程式碼)
val module = module {
    single<UserRepository>()
}

優點

  • 零程式碼變更 – 現有的 @Inject@Singleton 註解可直接使用
  • 逐步遷移 – 逐個模組進行遷移,混合使用 Hilt 與 Koin
  • 符合標準 – JSR-330 是 Java/Kotlin 標準,而非特定架構專有
  • 團隊熟悉度 – 開發人員已從 Hilt/Dagger 熟悉這些註解
  • 工具支援 – IDE 對 JSR-330 註解的支援

限制

少數 Hilt 特有的功能沒有直接對應的 JSR-330 等價項:

Hilt 功能Koin 做法
@HiltAndroidApp在 Application 中使用 @KoinApplication + startKoin<T>{}
@AndroidEntryPoint使用 by inject() 擴充方法
@HiltViewModel使用 @KoinViewModel
@ApplicationContextContext 參數(自動注入)
@ActivityContext基於作用域的 context 解析

後續步驟