原文地址:https://code.google.com/p/google-guice/wiki/Scopes
Scopes:作用域。
默认情况下,Guice每次在调用时都会返回一个新的实例,这种行为是可以通过作用域配置的,作用域允许复用对象实例。在一个应用服务的生命周期中,对象可能是单例的(@Singleton),也可能是一个回话的(@SessionScoped),也可能是一个请求的(@RequestScoped)。Guice在web应用中也包含一个servlet扩展的作用域。自定义作用域可以在不同类型的应用中使用。
Applying Scopes
作用域使用有不用的方式,例如注解, 1 2 3 4 | @Singleton public class InMemoryTransactionLog implements TransactionLog { /* everything here should be threadsafe! */ } |
也可以配置在代码里,
1 | bind(TransactionLog. class ).to(InMemoryTransactionLog. class ).in(Singleton. class ); |
也可以注解在@Provides方法处,
1 2 3 4 | @Provides @Singleton TransactionLog provideTransactionLog() { ... } |
如果有些冲突的作用域同时在一个类型上或者代码里的bind()方法上配置,那么bind()的配置生效。如果一个类型你不想给它设置要作用域,那么就绑定Scopes.NO_SCOPE。
像链接绑定那样,作用域应用于绑定的父类型,而不是绑定目标实现类。设想我们有一个同时实现Bar接口和Grill接口的实现类Applebees,这就要求需要同时绑定的这两种类型,一种是类型Bar,另一种是Grill:
1 2 | bind(Bar. class ).to(Applebees. class ).in(Singleton. class ); bind(Grill. class ).to(Applebees. class ).in(Singleton. class ); |
这是因为作用域应用于这个绑定的类型(Bar,Grill),而不是满足这个类型的实现类Applebees,为了允许只有一个实例,用一个注解@Singleton声明在父类型上,或者在代码里绑定。
1 | bind(Applebees. class ).in(Singleton. class ); |
这种绑定使得以上的其他两种.in(Singleton.class)语句不必要。这种in()语句还接受像RequestScoped.class或者是ServletScopes.REQUEST的注解:
1 2 3 | bind(UserPreferences. class ) .toProvider(UserPreferencesProvider. class ) .in(ServletScopes.REQUEST); |
这种注解是推荐优先的,因为它允许这个模块在不同的类型应用中复用,打个比方,一个被@RequestScoped注解的对象即能够在web应用的http请求中被使用,也可以在一个API服务器的rpc中被使用。
Eager Singletons
Eager Singletons(各种饥渴的单例):不是延迟的,是饿汉式的单例。 Guice有特殊的语法定义把单例为饿汉式的: 1 | 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对象,这是一个错误常识。如果你需要一个对象在一个较窄的范围内,注入该对象的提供者。