Search code examples
javadependency-injectionguice

Google Guice ProvisionException: Unable to provision. No implementation was bound


New to DI and guice..

I want to use a service (StoreLevelClient) This is a class defined by other team.

I inject this class in my main file like this:

class ClientAccessor {
    companion object {
        private val LOGGER = KotlinLogging.logger { }
    }

    private val myStoreLevelClient: StoreLevelClient =
        Guice.createInjector(ServiceModule()).getInstance(StoreLevelClient::class.java)

And made a module file for the StoreLevelClient like below:

class ServiceModule : AbstractModule() {

    @Provides
    @Singleton
    fun getClient(myServiceClient : KasServiceClient): StoreLevelClient {
        return StoreLevelClient(myServiceClient, AppConfigObject.trackedDocument, AppConfigObject.appConfigFallback)
    }

It gave me errors: Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors: 3 2022-05-20T18:27:50.800-07:00 1) No implementation for com.kasservice.KasServiceClient was bound. 4 2022-05-20T18:27:50.800-07:00 while locating com.kasservice.KasServiceClient 5 2022-05-20T18:27:50.800-07:00 for the 1st parameter of com.myservice.dependency.ServiceModule.getClient

The KasServiceClient is also from other's So I @Provides it in the ServiceModule as well:

@Provides
@Singleton
fun getService(
    cloudAuthCredentialVisitor: CloudAuthDefaultCredentialsVisitor,
    metricsAwareCallVisitor: MetricsAwareCallVisitor,
    @Named(BINGBONG_SERVICE_CLIENT_RETRY_STRATEGY)
    retryStrategy: RetryStrategy<*>
): KasServiceClient {
    val domain = AppConfig.findString(DOMAIN)
    val realm = AppConfig.getRealm().name()
    val qualifier = "$domain.$realm"
    return ClientBuilder()
            .remoteOf(KasServiceClient::class.java)
            .withConfiguration(qualifier)
            .withCallVisitors(cloudAuthCredentialVisitor, metricsAwareCallVisitor, CallAttachmentVisitor(Calls.retry(retryStrategy)))
            .newClient()
}

But it gave me errors like below:

Could not find a suitable constructor in com.amazon.coral.client.cloudauth.CloudAuthDefaultCredentialsVisitor. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
Could not find a suitable constructor in com.amazon.metrics.declarative.client.MetricsAwareCallVisitor. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.

The CloudAuthDefaultCredentialsVisitor and MetricsAwareCallVisitor are use @Provides and instantiate already. So I don't know why guice can't find them...??

Any idea for this?? I wonder I have some mistake when using Guice. But I have hard time to debug and find


Solution

  • Solved the problem by changing the way of inject:

    Instead use:

    class ClientAccessor {
        companion object {
            private val LOGGER = KotlinLogging.logger { }
        }
    
     private val myStoreLevelClient: StoreLevelClient =
            Guice.createInjector(ServiceModule()).getInstance(StoreLevelClient::class.java)
    

    Tried this:

    class ClientAccessor @Inject constructor(private val myStoreLevelClient: StoreLevelClient){
        companion object {
            private val LOGGER = KotlinLogging.logger { }
        }
    

    Reason:

    use @Inject instead of using the createInjector manually on particular modules, let guice inject it for us. When I tried to directly use createInjector in instantiating in my code, it will only lookup the specified module and not able to load other modules.