1. 依赖注入
1.1 类依赖注入
所谓的绑定就是将一个接口绑定到具体的类中,这样客户端不用关心具体的实现,而只需要获取相应的接口完成其服务即可。
helloworld.java
1 public interface helloworld {
2
3 string sayhello();
4 }
5
然后是具体的实现,helloworldimpl.java
1 public class helloworldimpl implements helloworld {
2
3 @override
4 public string sayhello() {
5 return "hello, world!";
6 }
7 }
8
写一个测试例子看看,helleworldtest.java
1 public class helleworldtest {
2
3 @test
4 public void testsayhello() {
5 injector inj= guice.createinjector(new module() {
6 @override
7 public void configure(binder binder) {
8 binder.bind(helloworld.class).to(helloworldimpl.class);
9 }
10 });
11 helloworld hw = inj.getinstance(helloworld.class);
12 assert.assertequals(hw.sayhello(), "hello, world!");
13 }
14 }
15
这个例子非常简单,通俗的将就是将一个helloworldimpl的实例与helloworld关联起来,当想guice获取一个helloworld实例的时候,guice就返回一个helloworldimpl的实例,然后我们就可以调用helloworld服务的方法了。
问题(1)helloworld是单例的么?测试下。
1 helloworld hw = inj.getinstance(helloworld.class);
2 assert.assertequals(hw.sayhello(), "hello, world!");
3 helloworld hw2 = inj.getinstance(helloworld.class);
4 system.out.println(hw.hashcode()"->"hw2.hashcode());
5 assert.assertequals(hw.hashcode(), hw2.hashcode());
解答(1)测试结果告诉我们,helloworld不是单例的,每次都会返回一个新的实例。
问题(2)helloworld的实例是helloworldimpl么?可以强制转型么?
helloworld hw = inj.getinstance(helloworld.class);
system.out.println(hw.getclass().getname());
解答(2),结果输出cn.imxylz.study.guice.helloworld.helloworldimpl,看来确实只是返回了一个正常的实例,并没有做过多的转换和代理。
问题(3),如果绑定多个实现到同一个接口上会出现什么情况?
1 public class helloworldimplagain implements helloworld {
2 @override
3 public string sayhello() {
4 return "hello world again.";
5 }
6 }
binder.bind(helloworld.class).to(helloworldimpl.class);
binder.bind(helloworld.class).to(helloworldimplagain.class);
解答(3),很不幸,guice目前看起来不允许多个实例绑定到同一个接口上了。
com.google.inject.creationexception: guice creation errors:
1) a binding to cn.imxylz.study.guice.helloworld.helloworld was already
configured at
cn.imxylz.study.guice.helloworld.helleworldtest$1.configure(helleworldtest.java:28).
at
cn.imxylz.study.guice.helloworld.helleworldtest$1.configure(helleworldtest.java:29)
问题(4),可以绑定一个实现类到实现类么?
1 injector inj= guice.createinjector(new module() {
2 @override
3 public void configure(binder binder) {
4 binder.bind(helloworldimpl.class).to(helloworldimpl.class);
5 }
6 });
7 helloworld hw = inj.getinstance(helloworldimpl.class);
8 system.out.println(hw.sayhello());
非常不幸,不可以自己绑定到自己。
1) binding points to itself.
at
cn.imxylz.study.guice.helloworld.helleworldtest$1.configure(helleworldtest.java:28)
我们来看看bind的语法。
<t> annotatedbindingbuilder<t> bind(class<t> type);
scopedbindingbuilder to(class extends t> implementation);
也就是说只能绑定一个类的子类到其本身。改造下,改用子类替代。
1 public class helloworldsubimpl extends helloworldimpl {
2
3 @override
4 public string sayhello() {
5 return "@helloworldsubimpl";
6 }
7 }
8
1 injector inj= guice.createinjector(new module() {
2 @override
3 public void configure(binder binder) {
4 binder.bind(helloworldimpl.class).to(helloworldsubimpl.class);
5 }
6 });
7 helloworldimpl hw = inj.getinstance(helloworldimpl.class);
8 system.out.println(hw.sayhello());
太好了,支持子类绑定,这样即使我们将一个实现类发布出去了(尽管不推荐这么做),我们在后期仍然有办法替换实现类。
使用bind有一个好处,由于java 5以上的泛型在编译器就确定了,所以可以帮我们检测出绑定错误的问题,而这个在配置文件中是无法检测出来的。
这样看起来module像是一个map,根据一个key获取其value,非常简单的逻辑。
问题(5),可以绑定到我们自己构造出来的实例么?
解答(5)当然可以!看下面的例子。
1 injector inj= guice.createinjector(new module() {
2 @override
3 public void configure(binder binder) {
4 binder.bind(helloworld.class).toinstance(new helloworldimpl());
5 }
6 });
7 helloworld hw = inj.getinstance(helloworld.class);
8 system.out.println(hw.sayhello());
问题(6),我不想自己提供逻辑来构造一个对象可以么?
解答(6),可以guice提供了一个方式(provider),允许自己提供构造对象的方式。
1 injector inj= guice.createinjector(new module() {
2 @override
3 public void configure(binder binder) {
4 binder.bind(helloworld.class).toprovider(new provider<helloworld>() {
5 @override
6 public helloworld get() {
7 return new helloworldimpl();
8 }
9 });
10 }
11 });
12 helloworld hw = inj.getinstance(helloworld.class);
13 system.out.println(hw.sayhello());
问题(7),实现类可以不经过绑定就获取么?比如我想获取helloworldimpl的实例而不通过module绑定么?
解答(7),可以,实际上guice能够自动寻找实现类。
injector inj= guice.createinjector();
helloworld hw = inj.getinstance(helloworldimpl.class);
system.out.println(hw.sayhello());
问题(8),可以使用注解方式完成注入么?不想手动关联实现类。
解答(8),好,guice提供了注解的方式完成关联。我们需要在接口上指明此接口被哪个实现类关联了。
1 @implementedby(helloworldimpl.class)
2 public interface helloworld {
3
4 string sayhello();
5 }
6
injector inj= guice.createinjector();
helloworld hw = inj.getinstance(helloworld.class);
system.out.println(hw.sayhello());
事实上对于一个已经被注解的接口我们仍然可以使用module来关联,这样获取的实例将是module关联的实例,而不是@implementedby注解关联的实例。这样仍然遵循一个原则,手动优于自动。
问题(9)再回头看问题(1)怎么绑定一个单例?
1 injector inj = guice.createinjector(new module() {
2
3 @override
4 public void configure(binder binder) {
5 binder.bind(helloworld.class).to(helloworldimplagain.class).in(scopes.singleton);
6 }
7 });
8 helloworld hw = inj.getinstance(helloworld.class);
9 helloworld hw2 = inj.getinstance(helloworld.class);
10 system.out.println(hw.hashcode() "->" hw2.hashcode());
11
可以看到现在获取的实例已经是单例的,不再每次请求生成一个新的实例。事实上guice提供两种scope,com.google.inject.scopes.singleton和com.google.inject.scopes.no_scope,所谓没有scope即是每次生成一个新的实例。
对于自动注入就非常简单了,只需要在实现类加一个singleton注解即可。
1 @singleton
2 public class helloworldimpl implements helloworld {
3
4 @override
5 public string sayhello() {
6 return "hello, world!";
7 }
8 }
9
附:【前沿】本教程的依赖注入部分基于老菜鸟叮咚的教程,原文在此。原文主要基于google guice 1.0版本的,本文基于google guice 2.0版本进行学习和讨论。
下一篇:
©2009-2014 imxylz
|求贤若渴