本章节继续讨论依赖注入的其他话题,包括作用域(scope,这里有一个与线程绑定的作用域例子)、立即初始化(Eagerly Loading Bindings)、运行阶段(Stage)、选项注入(Optional Injection)等等。
1.3.5 Scope(作用域)
在1.1章节中我们初步了解了对象的单例模式,在Guice中提供了一些常见的作用域,比如对于单例模式有下面两个作用域。
com.google.inject.Scopes.SINGLETON
com.google.inject.Scopes.NO_SCOPE
在使用上,可以使用Module的bind来实现,看下面的例子。
1
public
class
ScopeDemo {
2
public
static
void
main(String[] args) {
3
4
Service service
=
Guice.createInjector(
new
Module() {
5
@Override
6
public
void
configure(Binder binder) {
7
binder.bind(Service.
class
).to(WwwService.
class
).in(Scopes.SINGLETON);
8
}
9
}).getInstance(Service.
class
);
10
service.execute();
11
}
12
}
13
14
当然单例模式还可以似乎用@Singleton注解。
在com.google.inject.binder.ScopedBindingBuilder.in(Scope)方法中,一个Scope除了可以使上面的SINGLETION和NO_SCOPE外,还可以是自己定义的Scope。下面的例子演示了一个与线程绑定的Scope例子。
1
/**
2
* $Id: ThreadScopeDemo.java 90 2009-12-25 08:12:21Z xylz $
3
* xylz study project (www.imxylz.cn)
4
*/
5
package
cn.imxylz.study.guice.inject.more;
6
7
import
com.google.inject.Binder;
8
import
com.google.inject.Guice;
9
import
com.google.inject.Injector;
10
import
com.google.inject.Key;
11
import
com.google.inject.Module;
12
import
com.google.inject.Provider;
13
import
com.google.inject.Scope;
14
15
/**
a demo with thread-scope
16
*
@author
xylz (www.imxylz.cn)
17
*
@version
$Rev: 90 $
18
*/
19
public
class
ThreadScopeDemo {
20
21
static
class
ThreadServiceScope
implements
Scope {
22
23
static
ThreadLocal
<
Object
>
threadLocal
=
new
ThreadLocal
<
Object
>
();
24
25
@Override
26
public
<
T
>
Provider
<
T
>
scope(
final
Key
<
T
>
key,
final
Provider
<
T
>
unscoped) {
27
return
new
Provider
<
T
>
() {
28
@Override
29
public
T get() {
30
T instance
=
(T) threadLocal.get();
31
if
(instance
==
null
) {
32
instance
=
unscoped.get();
33
threadLocal.set(instance);
34
}
35
return
instance;
36
}
37
};
38
}
39
40
@Override
41
public
String toString() {
42
return
"
Scopes.ThreadServiceScope
"
;
43
}
44
}
45
46
public
static
void
main(String[] args) {
47
final
Injector inj
=
Guice.createInjector(
new
Module() {
48
@Override
49
public
void
configure(Binder binder) {
50
binder.bind(Service.
class
).to(WwwService.
class
).in(
new
ThreadServiceScope());
51
}
52
});
53
for
(
int
i
=
0
;i
<
3
;i
++
) {
54
new
Thread(
"
Thread-
"
+
i) {
55
public
void
run() {
56
for
(
int
m
=
0
;m
<3
;m
++
) {
57
System.out.println(String.format(
"
%s-%d:%d
"
,
//
58
getName()
//
59
,m
//
60
,inj.getInstance(Service.
class
).hashCode()));
61
try
{
62
Thread.sleep(
50L
);
63
}
catch
(Exception e) {
64
}
65
}
66
}
67
}.start();
68
}
69
}
70
}
71
注意,这里用到了《Google Guice 入门教程03 - 依赖注入》的中的两个类Service和WwwService。在本例中ThreadServiceScope类是一个与线程绑定的作用域(利用ThreadLocal特性),当当前线程中没有构造一个对象的时候先构造一个出来,然后放入线程上下文中,以后每次都从线程中获取对象。第50行是将WwwService服务以ThreadServiceScope的作用域绑定到Service服务上。第57-60行输出当前对象的hashCode,如果此类是同一对象的话就应该输出相同的hashCode。为了看到效果,我们使用3个线程,每个线程输出三次来看结果。
Thread-0-0:18303751
Thread-1-0:23473608
Thread-2-0:21480956
Thread-1-1:23473608
Thread-0-1:18303751
Thread-2-1:21480956
Thread-1-2:23473608
Thread-2-2:21480956
Thread-0-2:18303751
我们看到对于同一个线程(比如说Thread-0)的三次都输出了相同的对象(hashCode为18303751),而与线程2和线程3的hashCode不同。
(特别说明:如果两个线程输出了同一个hashCode不必惊慌,那是因为可能前一个线程生成的对象的地址空间被GC释放了,结果下一个线程使用了上一个线程的相同空间,所以这里使用Thread.sleep来降低这种可能性)
事实上在guice-servlet-2.0.jar中有与request和session绑定的scope。
com.google.inject.servlet.ServletScopes.REQUEST
com.google.inject.servlet.ServletScopes.SESSION
1.3.6 Eagerly Loading Bindings (立即初始化)
除了可以绑定scope外,对象默认在第一次调用时被创建,也即所谓的延时加载,Guice也允许对象在注入到Guice容器中时就被创建出来(显然这是针对单例模式才有效)。
1
public
class
EagerSingletonDemo {
2
3
public
EagerSingletonDemo() {
4
System.out.println(
"
constuctor:
"
+
System.nanoTime());
5
}
6
void
doit() {
7
System.out.println(
"
doit:
"
+
System.nanoTime());
8
}
9
public
static
void
main(String[] args)
throws
Exception{
10
Injector inj
=
Guice.createInjector(
new
Module() {
11
@Override
12
public
void
configure(Binder binder) {
13
binder.bind(EagerSingletonDemo.
class
).asEagerSingleton();
14
}
15
});
16
System.out.println(
"
before call:
"
+
System.nanoTime());
17
Thread.sleep(
100L
);
18
inj.getInstance(EagerSingletonDemo.
class
).doit();
19
}
20
}
结果输出如下:
constuctor:
26996967388652
before call:
26996967713635
doit:
26997069993702
可以看到我们的对象在调用getInstance之前就已经被构造出来了。
1.3.7 Stages (运行阶段)
Guice还有一个特效,可以指定Guice运行模式来控制Guice的加载速度。在com.google.inject.Stage枚举中提供了TOOL,DEVELOPMENT,PRODUCTION三种模式。
TOOL描述的是带有IDE等插件的运行模式;DEVELOPMENT是指在开发阶段只加载自己需要的功能(对于非立即初始化单例对象采用延后加载),这样来降低加载不需要功能的时间;而PRODUCTION模式是指完全加载所有功能(对于单例对象采用立即加载方式),这样可以更早的发现问题,免得等需要某些功能的时候才发现问题(要知道我们某些功能可能需要特定的条件才能触发)。
其实只有比较多的单例对象,并且单例对象构造比较耗时的情况下才能有用。大部分情况下这点性能可能都忽略不计了。
默认情况下Guice采用DEVELOPMENT模式。
1.3.8 Optional Injection (选项注入 )
选项注入描述的是如果不能从Guice容器中注入一个对象,那么可以使用一个默认的对象。看下面的例子。
1
public
class
OptionalInjectionDemo {
2
@Inject(optional
=
true
)
3
Service service
=
new
WwwService();
4
public
static
void
main(String[] args) {
5
Guice.createInjector(
new
Module() {
6
public
void
configure(Binder binder) {
7
//
binder.bind(Service.class).to(HomeService.class);
8
}
9
}).getInstance(OptionalInjectionDemo.
class
).service.execute();
10
}
11
}
上述例子中第2行描述的是选项注入,如果不能从Guice容器中获取一个Service服务那么就使用默认的WwwService,否则就是用获取的服务。如果将第7行注释去掉我们就可以看到实际上调用的是HomeService服务了。
到此为止,Guice依赖注入的基本教程就学习完了,下面的章节我们进入经典的AOP教程学习。