In Guice, what’s the difference between:
// Inside your AbstractModule subclass:
@Override
public void configure() {
bind(Service.class).to(ServiceImpl.class).in(Singleton.class);
}
And:
@Override
public void configure() {
bind(Service.class).to(ServiceImpl.class);
}
@Provides @Singleton
public ServiceImpl providesService() {
return new ServiceImpl();
}
Are they both the same? When would you use one versus the other? Thanks in advance.
They are nearly identical. The
@Singletonsyntax is useful for annotating@Providesmethods, or annotating the class itself (though I prefer to keep my scoping annotations inside modules).The difference lies in which key is marked Singleton, which has less to do with
@SingletonversusSingleton.class(orScopes.SINGLETON,asEagerSingleton,@Singletonclass annotations, ortoInstanceimplicit singletons) and more to do with what the default syntax makes easy. For example:Above we’ve bound interface
Ato classAImpl, and interfaceBto classBImpl, but the behavior is different:Awill retrieve the sameAImplinstance every time.AImplwill retrieve a differentAImplevery time, all of which are different thanA‘s instance.Bwill retrieve the sameBImplinstance every time.BImplwill also retrieve that sameBImplinstance thatBinjects.As you can see, each key is different, and Guice will allow multiple implementation instances if only the interface is bound with Singleton. If you only ever inject
AandBinterfaces, the behavior looks identical, but if you inject both interfaces and implementations from the same Injector, you may see differing behavior.Similar logic goes for
@Providesmethods:Cwill always return the sameCImplinstance.CImplwill create a newCImplevery time, unlessCImplhas no injectable public zero-arg constructor—then the injection will fail.Dwill always return the sameDImplinstance.DImplwill return a new instance every time, and each will be different than the one returned byD.Ewill return the sameEImplinstance every time.EImplwill also retrieve that same instanceEinjects.This provides some flexibility. Imagine a hypothetical
Cachethat keeps a certain number of most-recently-retrieved objects, where you want to have@User Cacheand@Product Cacheboth injectable. If youbind(Cache.class).in(Singleton.class), you will have one Cache shared between the objects (and any bareCacheinjections), whereas if youbind(Cache.class).annotatedWith(User.class).to(Cache.class).in(Singleton.class)then the annotated key is kept in singleton scope and each object type will have its own cache.