Guice 中作用域注解和作用域实例的区别

The difference between a scoping annotation and scope instances in Guice

在 Guice 中,当您指示某个实例的生命周期时,您将使用像 bind(Applebees.class).in(Singleton.class); 这样的作用域注释。 或者您似乎可以使用

之类的范围实例
bind(UserPreferences.class)
      .toProvider(UserPreferencesProvider.class)
      .in(ServletScopes.REQUEST);

并且 Guice 官方推荐前一种方法,因为前一种方法允许我们重用模块 class。 但我不确定这一点。我的假设是这样的,所以请检查是否正确。

Scope instances是Servelet的术语,所以如果你采用scope instances而不是scoping annotations,Module class只适用于Servelet。另一方面,如果您使用作用域注释,则可以重用您的模块 class 除非您放弃 Guice。

那么,这样对吗?你能查一下吗?

我相信你的理解是正确的,但对于什么样的重用会受到影响,有一点微妙之处。

您可能指的是 the Guice wiki on Scopes 中的这段文字(强调我的):

The in() clause accepts either a scoping annotation like RequestScoped.class and also Scope instances like ServletScopes.REQUEST:

bind(UserPreferences.class)
    .toProvider(UserPreferencesProvider.class)
    .in(ServletScopes.REQUEST);

The annotation is preferred because it allows the module to be reused in different types of applications. For example, an @RequestScoped object could be scoped to the HTTP request in a web app and to the RPC when it's in an API server.

即使使用 Guice 的 servlet-specific 作用域,您也可以在作用域实例 ServletScopes.REQUEST and the @RequestScoped annotation, and choose between in(Scope scope) and in(Class scopeAnnotation) accordingly (see ScopedBindingBuilder 之间进行选择。几乎每个范围都应该有一个相应的注释,因为它们在 类 和 @Provides 方法上特别有用。

重要的是要意识到这里总是一个Scope instance that actually implements the scoping behavior (specifically, wrapping an unscoped Provider so that it can return already-returned instances in the right conditions). To associate an annotation to a Scope instance, you need to make sure that a module calls bindScope, which accepts the Scope annotation class and the Scope instance; for Servlets, Guice has this binding automatically installed via InternalServletModule

@Override
protected void configure() {
  bindScope(RequestScoped.class, REQUEST);
  bindScope(SessionScoped.class, SESSION);
  // ...
}

那么使用 in(Class scopeAnnotation) 有什么好处? 当绑定到一个 Scope 实例时,您是在告诉 Guice 您想要使用哪个 Scope 实例,而不是允许用户有机会使用 bindScope 将注释绑定到不同的 Scope 实例。在我上面加粗的示例中,您可以想象使用相同的模块而不使用实际的 Guice servlet 扩展(注解除外),但是 只有绑定到注解 类 和然后自己打电话 bindScope。如果您使用 in(Scope) 进行绑定,则需要更改该行或编写一个新模块。

这对于您自己的自定义范围实例和注释尤其重要,因为它允许您在整个应用程序中一致地更改范围行为:

@Override public void configure() {
  // BAD: To change the scope, you'll need to change three lines.
  // If you don't change all three together, you'll get inconsistent behavior.
  bind(A.class).to(AImpl.class).in(MyScope.INSTANCE);
  bind(B.class).to(BImpl.class).in(MyScope.INSTANCE);
  bindScope(AScoped.class, MyScope.INSTANCE);
}

@Override public void configure() {
  // GOOD: To change the scope, you can change one line, and optionally
  // extract that line to a separate Module.
  bind(A.class).to(AImpl.class).in(AScoped.class);
  bind(B.class).to(BImpl.class).in(AScoped.class);
  bindScope(AScoped.class, MyScope.INSTANCE);
}