Guice: Singleton.class和@Singleton之间的区别

29
在Guice中,以下两者有什么区别:
// Inside your AbstractModule subclass:
@Override
public void configure() {
    bind(Service.class).to(ServiceImpl.class).in(Singleton.class);
}

而且:

@Override
public void configure() {
    bind(Service.class).to(ServiceImpl.class);
}

@Provides @Singleton
public ServiceImpl providesService() {
    return new ServiceImpl();
}

它们是一样的吗?什么情况下会使用一个而不是另一个?谢谢!

1个回答

53

它们几乎相同。 @Singleton语法对于注释@Provides方法或注释类本身非常有用(尽管我更喜欢将我的作用域注释放在模块内部)。

区别在于标记为Singleton的键不同,这与@SingletonSingleton.class(或Scopes.SINGLETONasEagerSingleton@Singleton类注释或toInstance隐式单例)无关,而与默认语法使其易于实现有关。例如:

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(); }
}

以上,我们已经将接口A绑定到类AImpl,将接口B绑定到类BImpl,但它们的行为不同:

  • 注入A每次都会检索相同的AImpl实例。
  • 注入AImpl每次都会检索不同的AImpl实例,这些实例与A的实例不同。
  • 注入B每次都会检索相同的BImpl实例。
  • 注入BImpl也会检索与B注入的相同的BImpl实例。

如您所见,每个键都是不同的,Guice只有在单例模式下绑定接口时才允许多个实现实例。如果您只注入AB接口,则其行为看起来相同,但如果您从同一Injector中同时注入接口和实现,则可能会看到不同的行为。

@Provides方法的逻辑类似:

  • 注入C始终返回相同的CImpl实例。
  • 注入CImpl每次都会创建一个新的CImpl实例,除非CImpl没有可注入的公共无参构造函数-然后注入将失败。
  • 注入D始终返回相同的DImpl实例。
  • 注入DImpl每次都会返回一个新的实例,并且每个实例都与D返回的不同。
  • 注入E每次都会返回相同的EImpl实例。
  • 注入EImpl也会检索与E注入的相同实例。

这提供了一些灵活性。想象一下一个假想的Cache,它保存最近检索的一定数量的对象,您希望@User Cache@Product Cache都是可注入的。如果您bind(Cache.class).in(Singleton.class),则在对象之间(以及任何裸露的Cache注入)共享一个Cache,而如果您bind(Cache.class).annotatedWith(User.class).to(Cache.class).in(Singleton.class),则注释键被保留在单例范围内,每个对象类型将有自己的缓存。


我有一个这样的情况,它们似乎表现不同。使用绑定语句没有限制到只有1个实例。然而,使用@Singleton就可以解决这个问题。 - EFreak
@EFreak:更新了答案。这并不完全是语法上的区别,但重新审视问题后,有一些微妙之处。 - Jeff Bowman
非常感谢!这正是我昨天遇到的问题。不过,我有一个编辑建议。会更新答案。 - EFreak
我有点理解这个。在我的情况下,我有一个具体类LoggingConfiguration绑定如下:bind(LoggingConfiguration.class).in(Singleton.class); injector = Guice.createInjector(bindingModule); return injector.getInstance(ProductsLogging.class).getLogger(clazz);那么在整个虚拟机中只会有一个吗? - ggb667
假设ProductsLogging注入LoggingConfiguration,那么我猜测(从你告诉我的很少的信息中)每个LoggingConfiguration的注入都是相同的实例,包括在ProductsLogging中。关于getLogger返回的记录器,我无法告诉你,因为这取决于ProductsLogging的实现。如果您想要更深入的分析和更多细节,我认为您最好通过提出一个单独的问题来获得更好的服务,并请确保包括有关如何注入LoggingConfiguration以及您想要仅有一个实例的类的代码。 - Jeff Bowman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接