Guice系列之用户指南(十)

原文地址:https://code.google.com/p/google-guice/wiki/Scopes

Scopes:作用域。

默认情况下,Guice每次在调用时都会返回一个新的实例,这种行为是可以通过作用域配置的,作用域允许复用对象实例。在一个应用服务的生命周期中,对象可能是单例的(@Singleton),也可能是一个回话的(@SessionScoped),也可能是一个请求的(@RequestScoped)。Guice在web应用中也包含一个servlet扩展的作用域。自定义作用域可以在不同类型的应用中使用。

Applying Scopes
作用域使用有不用的方式,例如注解,

@Singleton
public class InMemoryTransactionLog implements TransactionLog {
  /* everything here should be threadsafe! */
}

也可以配置在代码里,

  bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);

也可以注解在@Provides方法处,

@Provides @Singleton
  TransactionLog provideTransactionLog() {
    ...
  }

如果有些冲突的作用域同时在一个类型上或者代码里的bind()方法上配置,那么bind()的配置生效。如果一个类型你不想给它设置要作用域,那么就绑定Scopes.NO_SCOPE。

像链接绑定那样,作用域应用于绑定的父类型,而不是绑定目标实现类。设想我们有一个同时实现Bar接口和Grill接口的实现类Applebees,这就要求需要同时绑定的这两种类型,一种是类型Bar,另一种是Grill:

  bind(Bar.class).to(Applebees.class).in(Singleton.class);
  bind(Grill.class).to(Applebees.class).in(Singleton.class);

这是因为作用域应用于这个绑定的类型(Bar,Grill),而不是满足这个类型的实现类Applebees,为了允许只有一个实例,用一个注解@Singleton声明在父类型上,或者在代码里绑定。

 bind(Applebees.class).in(Singleton.class);

这种绑定使得以上的其他两种.in(Singleton.class)语句不必要。这种in()语句还接受像RequestScoped.class或者是ServletScopes.REQUEST的注解:

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

这种注解是推荐优先的,因为它允许这个模块在不同的类型应用中复用,打个比方,一个被@RequestScoped注解的对象即能够在web应用的http请求中被使用,也可以在一个API服务器的rpc中被使用。

Eager Singletons
Eager Singletons(各种饥渴的单例):不是延迟的,是饿汉式的单例。
Guice有特殊的语法定义把单例为饿汉式的:

bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();

Guice有特殊的语法定义把单例为饥渴的:

饿汉式的单例可以很快揭示初始化问题,并确保最终用户获得一致的,直观的体验。懒汉式的单例保证了一个快速的编辑-完成-启动的开发周期,用这个Stage的枚举可以区分那种策略被使用:

PRODUCTION DEVELOPMENT
.asEagerSingleton() eager eager
.in(Singleton.class) eager lazy
.in(Scopes.SINGLETON) eager lazy
@Singleton eager* lazy

Guice会在已知类型的情况下创建饿汉式单例,这些类型都是在自己的模块,并加上这些类型递归依赖提到的类型。

Choosing a scope

选择一种作用域,如果一个对象是有状态的,那么这个作用域就明显了,每个应用都是@Singleton的,每个请求都是@RequestScoped等等。如果一个对象是没有状态的,并且创建开销是很小的,那么作用域就没有必要,就不用绑定作用域,Guice会按需要创建不同的实例。

许多单例在java应用中是很流行的但是但它们没有提供多大的价值,尤其是当涉及依赖注入。尽管单例可以节省对象创建开销,或者晚一点的垃圾回收,获取一个单例的句柄得到需要同步。单例是很有用的:
有状态的对象,例如配置或者计数
创建昂贵或者查找昂贵的对象
占用资源,如数据库连接池对象

Scopes and Concurrency

作用域和并发,类型被@Singleton和@SessionScoped注解的一定是线程安全的,任何被注入到这些类型的也一定是线程安全的,减少可变性来限制一些需要并发保护的状态。
@RequestScoped对象不需要线程安全,一个@Singleton和@SessionScoped对象来依赖@RequestScoped对象,这是一个错误常识。如果你需要一个对象在一个较窄的范围内,注入该对象的提供者。

此条目发表在编程语言分类目录,贴了, 标签。将固定链接加入收藏夹。