Guice: differences between Singleton.class and @Singleton
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 will you use one against the other? Thanks in advance.
source to share
They are almost identical. The syntax @Singleton
is useful for annotating methods @Provides
or annotating the class itself (although I prefer to keep my scoped annotations inside modules).
The difference is that the key is marked singleton, which is less linked to @Singleton
anti Singleton.class
(or Scopes.SINGLETON
, asEagerSingleton
, @Singleton
annotation class or toInstance
implicit singletons) and more to do with the fact that simplifies the default syntax. For example:
public class MyModule extends AbstractModule {
@Override public void configure() {
bind(A.class).to(AImpl.class).in(Singleton.class);
bind(B.class).to(BImpl.class);
bind(BImpl.class).in(Singleton.class);
}
@Provides @Singleton C provideC() { return new CImpl(); }
@Provides @Singleton D provideD(DImpl dImpl) { return dImpl; }
@Provides E provideE(EImpl eImpl) { return eImpl; }
@Provides @Singleton EImpl provideEImpl() { return new EImpl(); }
}
Above we have bound an interface A
to a class AImpl
and an interface B
to a class BImpl
, but the behavior is different:
- The injection
A
will fetch the same instanceAImpl
every time. - Injecting
AImpl
will get different each timeAImpl
, all of which are different from the instanceA
. - The injection
B
will fetch the same instanceBImpl
every time. - The injection
BImpl
will also retrieve the sameBImpl
instance that itB
inserts.
As you can see, each key is different and Guice allows multiple instances of the implementation as long as the interface is associated with a Singleton. If you only inject interfaces A
and B
, the behavior looks the same, but if you inject both interfaces and implementations from the same Injector, you can see different behavior.
Similar logic for methods @Provides
:
- Input
C
always returns the same instanceCImpl
. - Injection
CImpl
will create a newCImpl
one every time, unlessCImpl
it has an injectable public constructor with a null-arg argument, then the injection will fail. - Injection
D
always returns the same instanceDImpl
. - The input
DImpl
will return a new instance each time and each will be different from the one that is returnedD
. - Injection
E
will return the same instanceEImpl
every time. - Input
EImpl
will also retrieve the same instanceE
.
This provides some flexibility. Imagine a hypothetical Cache
in which to store a certain number of objects, which have recently been extracted, where you want to have @User Cache
and @Product Cache
both injection. If you do bind(Cache.class).in(Singleton.class)
, you will have one shared cache between objects (and any naked Cache
injection), whereas if you do bind(Cache.class).annotatedWith(User.class).to(Cache.class).in(Singleton.class)
, then the annotated key will be stored in a one-point scope and each type of object will have its own cache.
source to share