JSR-330 Compatibility
Koin provides full JSR-330 (Jakarta Inject) compatibility through Koin Annotations and the Kotlin Compiler Plugin. This makes Koin an excellent choice for teams migrating from Hilt, Dagger, or other JSR-330 compatible frameworks.
Why JSR-330?
JSR-330 defines a standard set of annotations for dependency injection in Java/Kotlin:
| JSR-330 Annotation | Koin Equivalent | Purpose |
|---|---|---|
@Inject | Constructor detection | Mark injectable constructors |
@Singleton | @Single | Single instance scope |
@Named | @Named | String-based qualifier |
@Qualifier | @Qualifier | Custom qualifier annotations |
@Scope | @Scope | Custom scope annotations |
Setup
Add the koin-jsr330 dependency to your project:
dependencies {
implementation("io.insert-koin:koin-jsr330:$koin_version")
}
Basic Usage
@Singleton and @Inject
Your existing Hilt/Dagger classes work with Koin:
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 Qualifiers
String-based qualifiers work identically:
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
)
Custom @Qualifier Annotations
Keep your existing custom qualifiers:
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 annotations work with Koin's ViewModel support:
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)
// ...
}
}
}
Use @KoinViewModel (Koin annotation) for ViewModels. JSR-330 doesn't define a ViewModel annotation.
Migration from Hilt
Before (Hilt)
@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()
}
After (Koin with JSR-330)
// Application - use startKoin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApplication)
modules(appModule)
}
}
}
// Activity - use by inject()
class MainActivity : AppCompatActivity() {
private val analytics: Analytics by inject()
}
// Keep @Singleton and @Inject unchanged!
@Singleton
class Analytics @Inject constructor(
private val context: Context // Koin provides Context automatically
)
// Module with @Single for provided instances
@Module
@ComponentScan
class NetworkModule {
@Single
fun provideRetrofit(): Retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
Mixed Usage
You can freely mix JSR-330 and Koin annotations in the same project:
// JSR-330 style
@Singleton
@Named("local")
class LocalDatabase @Inject constructor() : Database
// Koin style
@Single
@Named("remote")
class RemoteDatabase(private val api: ApiService) : Database
// Mixed - Koin annotation with JSR-330 injection
@Factory
class DatabaseManager @Inject constructor(
@Named("local") private val local: Database,
@Named("remote") private val remote: Database
)
Compiler Plugin Support
The Koin Compiler Plugin also recognizes JSR-330 annotations:
// These are equivalent with Compiler Plugin
@Singleton
class UserRepository @Inject constructor(private val api: ApiService)
// Compiler Plugin DSL (generates same code)
val module = module {
single<UserRepository>()
}
Benefits
- Zero code changes - Existing
@Injectand@Singletonannotations work as-is - Gradual migration - Migrate module by module, mixing Hilt and Koin
- Standard compliance - JSR-330 is a Java/Kotlin standard, not framework-specific
- Team familiarity - Developers know these annotations from Hilt/Dagger
- Tooling support - IDE support for JSR-330 annotations
Limitations
A few Hilt-specific features don't have direct JSR-330 equivalents:
| Hilt Feature | Koin Approach |
|---|---|
@HiltAndroidApp | @KoinApplication + startKoin<T>{} in Application |
@AndroidEntryPoint | by inject() extension functions |
@HiltViewModel | @KoinViewModel |
@ApplicationContext | Context parameter (auto-injected) |
@ActivityContext | Scope-based context resolution |
Next Steps
- Hilt Migration Guide - Complete migration walkthrough
- Qualifiers - All qualifier options in Koin
- Definitions - Full JSR-330 annotation reference