Search code examples
javakotlindependency-injectionguice

Guice: use the same instance independently and inside the binder


Is it possible to create one singleton object, and being able to inject it independently as the one instance, or as a part of set or map?

For example, I have two instaces like this:

@Provides
@Singleton
@Named(value = "bike.listener")
fun bikeListener(
    mapper: ObjectMapper,
    amazonSNS: AmazonSNS,
    kafkaPublisher: KafkaPublisher
): PublishingListener {
    return PublishingListener(
        mapper = mapper,
        amazonSns = amazonSNS,
        kafkaPublisher = kafkaPublisher
    )
}

@Provides
@Singleton
@Named(value = "car.listener")
fun carListener(
    mapper: ObjectMapper,
    amazonSNS: AmazonSNS,
    kafkaPublisher: KafkaPublisher
): PublishingListener {
    return PublishingListener(
        mapper = mapper,
        amazonSns = amazonSNS,
        kafkaPublisher = kafkaPublisher,
    )
}

... and I use them in my classes by directly injecting them via @Named parameters. But, I also have a use case where I want to inject both of them in a set.

So something like:

@Inject
Set<PublishingListener> listeners; // carListener and bikeListener here

Is it possible to create MultiBinder which would use the already defined instances? How can I use the same instance in the set, and separately?


Solution

  • In your Guice Module, you can bind those instances to a set like this:

    @Provides
    fun publisherListenerSet(
        @Named("car.listener") val carListener: PublisherListener,
        @Named("bike.listener") val bikeListener: PublisherListener
    ): Set<PublisherListener> { 
        setOf(carListener, bikeListener)
    }
    

    Or you can use:

    protected override fun configure() {
        val publishingListenerSetBinder = Multibinder.newSetBinder(binder(), PublisherListener::class.java)
        publishingListenerSetBinder.addBinding().annotatedWith(Names.named("car.listener"))!!.to(PublishingListener::class.java)
        publishingListenerSetBinder.addBinding().annotatedWith(Names.named("bike.listener"))!!.to(PublishingListener::class.java)
    }
    

    As you don’t actually call the provider method, the Injector can maintain the singleton-ness of the thing that the provider provides.