1.3 更多话题
1.3.1 接口多实现
如果一个接口有多个实现,这样通过@Inject和Module都难以直接实现,但是这种现象确实是存在的,于是Guice提供了其它注入方式来解决此问题。比如下面的自定义注解。
1
public
interface
Service {
2
3
void
execute();
4
}
5
6
1
public
class
HomeService
implements
Service {
2
@Override
3
public
void
execute() {
4
System.out.println(
"
home.imxylz.cn
"
);
5
}
6
}
1
public
class
WwwService
implements
Service {
2
@Override
3
public
void
execute() {
4
System.out.println(
"
www.imxylz.cn
"
);
5
}
6
}
1
@Retention(RetentionPolicy.RUNTIME)
2
@Target({FIELD,PARAMETER})
3
@BindingAnnotation
4
public
@
interface
Home {
5
}
1
@Retention(RetentionPolicy.RUNTIME)
2
@Target({FIELD,PARAMETER})
3
@BindingAnnotation
4
public
@
interface
Www {
5
}
上面的代码描述的是一个Service服务,有WwwService和HomeService两个实现,同时有Www和Home两个注解(如果对注解各个参数不明白的需要单独去学习JAVA 5注解)。好了下面请出我们的主角。
1
/**
2
* $Id: MultiInterfaceServiceDemo.java 82 2009-12-24 06:55:16Z 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.Inject;
10
import
com.google.inject.Module;
11
12
/**
a demo with multi interfaces
13
*
@author
xylz (www.imxylz.cn)
14
*
@version
$Rev: 82 $
15
*/
16
public
class
MultiInterfaceServiceDemo {
17
@Inject
18
@Www
19
private
Service wwwService;
20
@Inject
21
@Home
22
private
Service homeService;
23
public
static
void
main(String[] args) {
24
MultiInterfaceServiceDemo misd
=
Guice.createInjector(
new
Module() {
25
@Override
26
public
void
configure(Binder binder) {
27
binder.bind(Service.
class
).annotatedWith(Www.
class
).to(WwwService.
class
);
28
binder.bind(Service.
class
).annotatedWith(Home.
class
).to(HomeService.
class
);
29
}
30
}).getInstance(MultiInterfaceServiceDemo.
class
);
31
misd.homeService.execute();
32
misd.wwwService.execute();
33
}
34
}
35
36
此类的结构是注入两个Service服务,其中wwwService是注入@Www注解关联的WwwService服务,而homeService是注入@Home注解关联的HomeService服务。
同样关于此结构我们要问几个问题。
问题(1)静态注入多个服务怎么写?
其实,参照教程02,我们可以使用下面的例子。
1
public
class
StaticMultiInterfaceServiceDemo {
2
@Inject
3
@Www
4
private
static
Service wwwService;
5
@Inject
6
@Home
7
private
static
Service homeService;
8
public
static
void
main(String[] args) {
9
Guice.createInjector(
new
Module() {
10
@Override
11
public
void
configure(Binder binder) {
12
binder.bind(Service.
class
).annotatedWith(Www.
class
).to(WwwService.
class
);
13
binder.bind(Service.
class
).annotatedWith(Home.
class
).to(HomeService.
class
);
14
binder.requestStaticInjection(StaticMultiInterfaceServiceDemo.
class
);
15
}
16
});
17
StaticMultiInterfaceServiceDemo.homeService.execute();
18
StaticMultiInterfaceServiceDemo.wwwService.execute();
19
}
20
}
问题(2):如果不小心一个属性绑定了多个接口怎么办?
非常不幸,你将得到类似一下的错误,也就是说不可以绑定多个服务。
1
) cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService has more than one annotation annotated with @BindingAnnotation: cn.imxylz.study.guice.inject.more.Www and cn.imxylz.study.guice.inject.more.Home
at cn.imxylz.study.guice.inject.more.StaticMultiInterfaceServiceDemo.wwwService(StaticMultiInterfaceServiceDemo.java:
17
)
问题(3):我太懒了不想写注解来区分多个服务,怎么办?
程序员都是懒惰的,于是Google帮我们提供了一个Names的模板来生成注解。看下面的例子。
1
public
class
NoAnnotationMultiInterfaceServiceDemo {
2
@Inject
3
@Named(
"
Www
"
)
4
private
static
Service wwwService;
5
@Inject
6
@Named(
"
Home
"
)
7
private
static
Service homeService;
8
public
static
void
main(String[] args) {
9
Guice.createInjector(
new
Module() {
10
@Override
11
public
void
configure(Binder binder) {
12
binder.bind(Service.
class
).annotatedWith(Names.named(
"
Www
"
)).to(WwwService.
class
);
13
binder.bind(Service.
class
).annotatedWith(Names.named(
"
Home
"
)).to(HomeService.
class
);
14
binder.requestStaticInjection(NoAnnotationMultiInterfaceServiceDemo.
class
);
15
}
16
});
17
NoAnnotationMultiInterfaceServiceDemo.homeService.execute();
18
NoAnnotationMultiInterfaceServiceDemo.wwwService.execute();
19
}
20
}
上面的例子中我们使用Named来标注我们的服务应该使用什么样的注解,当然前提是我们已经将相应的服务与注解关联起来了。
1.3.2 Provider注入
在教程第一篇中我们提到了可以通过Provider注入一个服务,这里详细说说这种模式。
首先我们需要构造一个Provider<T>出来。
1
public
class
WwwServiceProvider
implements
Provider
<
Service
>
{
2
3
@Override
4
public
Service get() {
5
return
new
WwwService();
6
}
7
}
8
9
上面的Provider的意思很简单,每次新建一个新的WwwService对象出来。
注入的过程看下面的代码。
1
public
class
ProviderServiceDemo {
2
3
@Inject
4
private
Service service;
5
6
public
static
void
main(String[] args) {
7
Injector inj
=
Guice.createInjector(
new
Module() {
8
@Override
9
public
void
configure(Binder binder) {
10
binder.bind(Service.
class
).toProvider(WwwServiceProvider.
class
);
11
}
12
});
13
ProviderServiceDemo psd
=
inj.getInstance(ProviderServiceDemo.
class
);
14
psd.service.execute();
15
}
16
17
}
18
19
很显然如果这东西和线程绑定就非常好了,比如我们可以使用ThreadLocal来做线程的对象交换。
当然如果想自动注入(不使用Module手动关联)服务的话,可以使用@ProviderBy注解。
1
@ProvidedBy(WwwServiceProvider.
class
)
2
public
interface
Service {
3
4
void
execute();
5
}
6
7
这样我们就不必使用Module将Provider绑定到Service上,获取服务就很简单了。
ProviderServiceDemo psd
=
Guice.createInjector().getInstance(ProviderServiceDemo.
class
);
psd.service.execute();
除了上述两种方式我们还可以注入Provider,而不是注入服务,比如下面的例子例子中,属性不再是Service,而是一个Provider<Service>。
1
public
class
ProviderServiceDemo {
2
3
@Inject
4
private
Provider
<
Service
>
provider;
5
6
public
static
void
main(String[] args) {
7
ProviderServiceDemo psd
=
Guice.createInjector(
new
Module() {
8
@Override
9
public
void
configure(Binder binder) {
10
binder.bind(Service.
class
).toProvider(WwwServiceProvider.
class
);
11
}
12
}).getInstance(ProviderServiceDemo.
class
);
13
psd.provider.get().execute();
14
}
15
}
16
17
当然了,由于我们WwwServiceProvider每次都是构造一个新的服务出来,因此在类ProviderServiceDemo中的provider每次获取的服务也是不一样的。
1.3.3 绑定常量
看看下面的例子,演示了一个绑定整数值到实例的例子。
1
public
class
ConstantInjectDemo {
2
3
@Inject
4
@Named(
"
v
"
)
5
private
int
v;
6
public
static
void
main(String[] args) {
7
8
ConstantInjectDemo cid
=
Guice.createInjector(
new
Module() {
9
@Override
10
public
void
configure(Binder binder) {
11
binder.bindConstant().annotatedWith(Names.named(
"
v
"
)).to(
12
);
12
}
13
}).getInstance(ConstantInjectDemo.
class
);
14
System.out.println(cid.v);
15
}
16
}
17
18
当然,既然可以使用Named,也就可以使用自己写注解了。但是看起来好像没有多大作用。除了上述写法,也可以用下面的方式实现。
binder.bind(int.class).annotatedWith(Names.named("v")).toInstance(12);
除了可以绑定int外,在ConstantBindingBuilder类中还可以绑定其它的基本类型。
com.google.inject.binder.ConstantBindingBuilder.to(String)
com.google.inject.binder.ConstantBindingBuilder.to(
long
)
com.google.inject.binder.ConstantBindingBuilder.to(
boolean
)
com.google.inject.binder.ConstantBindingBuilder.to(
double
)
com.google.inject.binder.ConstantBindingBuilder.to(
float
)
com.google.inject.binder.ConstantBindingBuilder.to(
short
)
com.google.inject.binder.ConstantBindingBuilder.to(
char
)
1.3.4 绑定Properties
除了可以绑定基本类型外,还可以绑定一个Properties到Guice中,当然了,由于Properties本质上时一个Map<String,String>,因此Guice也允许绑定一个Map<String,String>。
1
@Inject
2
@Named(
"
web
"
)
3
private
String web;
4
5
public
static
void
main(String[] args) {
6
7
ConstantInjectDemo cid
=
Guice.createInjector(
new
Module() {
8
@Override
9
public
void
configure(Binder binder) {
10
Properties properties
=
new
Properties();
11
properties.setProperty(
"
web
"
,
"
www.imxylz.cn
"
);
12
Names.bindProperties(binder, properties);
13
}
14
}).getInstance(ConstantInjectDemo.
class
);
15
System.out.println(cid.web);
16
}
17
18