kotlindependency-injectionmicronaut

How to make a standard Java class' instance available as a bean?


I have a Micronaut controller that takes the java.time.Clock as a dependency:

@Controller("/foo")
class FooListener (
    private val clock: Clock,
) {
    // ...
}

IntelliJ warns:

Unsatisfied dependency: no bean matches the injection point
Inspection info: Reports injection points in bean classes that have missing or ambiguous dependencies.

and the application does not start:

Error starting Micronaut server: Bean definition [FooListener] could not be loaded: Failed to inject value for parameter [clock] of class: FooListener
Message: No bean of type [java.time.Clock] exists.

I've tried a @PostConstruct in the main application instance:

object Application {
    @JvmStatic
    fun main(args: Array<String>) {
        Micronaut.build()
            .eagerInitSingletons(true)
            .eagerInitConfiguration(true)
            .mainClass(Application.javaClass)
            .start()
    }

    @PostConstruct
    fun onStart(context: ApplicationContext) {
        context.registerSingleton(Clock::class.java, Clock.systemUTC())
    }
}

but that seemed to get ignored; maybe code in main runs first and the problem happens before the @PostConstruct runs.

Adding this little wrapper class did not help either:

class ClockConfig {
    @Bean
    fun clock(): Clock = Clock.systemUTC()
}

When I add @io.micronaut.context.annotation.Configuration to that ClockConfig class, the compiler complains that "This annotation is not applicable to target 'class'", and it looks like it applies to packages.

How can I put a Clock in the application context with Clock.systemUTC() as the value?


Solution

  • Seems like a Factory does the trick:

    import io.micronaut.context.annotation.Factory
    import jakarta.inject.Singleton
    import java.time.Clock
    
    @Factory
    class ClockFactory {
        @Singleton
        fun clock(): Clock = Clock.systemUTC()
    }