1. 依賴注入
1.1 類依賴注入
所謂的綁定就是將一個(gè)接口綁定到具體的類中,這樣客戶端不用關(guān)心具體的實(shí)現(xiàn),而只需要獲取相應(yīng)的接口完成其服務(wù)即可。
HelloWorld.java
1 public interface HelloWorld {
2
3 String sayHello();
4 }
5
然后是具體的實(shí)現(xiàn),HelloWorldImpl.java
1 public class HelloWorldImpl implements HelloWorld {
2
3 @Override
4 public String sayHello() {
5 return "Hello, world!";
6 }
7 }
8
寫一個(gè)測(cè)試?yán)涌纯矗?em>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
這個(gè)例子非常簡(jiǎn)單,通俗的將就是將一個(gè)HelloWorldImpl的實(shí)例與HelloWorld關(guān)聯(lián)起來(lái),當(dāng)想Guice獲取一個(gè)HelloWorld實(shí)例的時(shí)候,Guice就返回一個(gè)HelloWorldImpl的實(shí)例,然后我們就可以調(diào)用HelloWorld服務(wù)的方法了。
問(wèn)題(1)HelloWorld是單例的么?測(cè)試下。
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)測(cè)試結(jié)果告訴我們,HelloWorld不是單例的,每次都會(huì)返回一個(gè)新的實(shí)例。
問(wèn)題(2)HelloWorld的實(shí)例是HelloWorldImpl么?可以強(qiáng)制轉(zhuǎn)型么?
HelloWorld hw = inj.getInstance(HelloWorld.class);
System.out.println(hw.getClass().getName());
解答(2),結(jié)果輸出cn.imxylz.study.guice.helloworld.HelloWorldImpl,看來(lái)確實(shí)只是返回了一個(gè)正常的實(shí)例,并沒(méi)有做過(guò)多的轉(zhuǎn)換和代理。
問(wèn)題(3),如果綁定多個(gè)實(shí)現(xiàn)到同一個(gè)接口上會(huì)出現(xiàn)什么情況?
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目前看起來(lái)不允許多個(gè)實(shí)例綁定到同一個(gè)接口上了。
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)
問(wèn)題(4),可以綁定一個(gè)實(shí)現(xiàn)類到實(shí)現(xiàn)類么?
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)
我們來(lái)看看bind的語(yǔ)法。
<T> AnnotatedBindingBuilder<T> bind(Class<T> type);
ScopedBindingBuilder to(Class<? extends T> implementation);
也就是說(shuō)只能綁定一個(gè)類的子類到其本身。改造下,改用子類替代。
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());
太好了,支持子類綁定,這樣即使我們將一個(gè)實(shí)現(xiàn)類發(fā)布出去了(盡管不推薦這么做),我們?cè)诤笃谌匀挥修k法替換實(shí)現(xiàn)類。
使用bind有一個(gè)好處,由于JAVA 5以上的泛型在編譯器就確定了,所以可以幫我們檢測(cè)出綁定錯(cuò)誤的問(wèn)題,而這個(gè)在配置文件中是無(wú)法檢測(cè)出來(lái)的。
這樣看起來(lái)Module像是一個(gè)Map,根據(jù)一個(gè)Key獲取其Value,非常簡(jiǎn)單的邏輯。
問(wèn)題(5),可以綁定到我們自己構(gòu)造出來(lái)的實(shí)例么?
解答(5)當(dāng)然可以!看下面的例子。
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());
問(wèn)題(6),我不想自己提供邏輯來(lái)構(gòu)造一個(gè)對(duì)象可以么?
解答(6),可以Guice提供了一個(gè)方式(Provider<T>),允許自己提供構(gòu)造對(duì)象的方式。
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());
問(wèn)題(7),實(shí)現(xiàn)類可以不經(jīng)過(guò)綁定就獲取么?比如我想獲取HelloWorldImpl的實(shí)例而不通過(guò)Module綁定么?
解答(7),可以,實(shí)際上Guice能夠自動(dòng)尋找實(shí)現(xiàn)類。
Injector inj= Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
System.out.println(hw.sayHello());
問(wèn)題(8),可以使用注解方式完成注入么?不想手動(dòng)關(guān)聯(lián)實(shí)現(xiàn)類。
解答(8),好,Guice提供了注解的方式完成關(guān)聯(lián)。我們需要在接口上指明此接口被哪個(gè)實(shí)現(xiàn)類關(guān)聯(lián)了。
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());
事實(shí)上對(duì)于一個(gè)已經(jīng)被注解的接口我們?nèi)匀豢梢允褂肕odule來(lái)關(guān)聯(lián),這樣獲取的實(shí)例將是Module關(guān)聯(lián)的實(shí)例,而不是@ImplementedBy注解關(guān)聯(lián)的實(shí)例。這樣仍然遵循一個(gè)原則,手動(dòng)優(yōu)于自動(dòng)。
問(wèn)題(9)再回頭看問(wèn)題(1)怎么綁定一個(gè)單例?
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
可以看到現(xiàn)在獲取的實(shí)例已經(jīng)是單例的,不再每次請(qǐng)求生成一個(gè)新的實(shí)例。事實(shí)上Guice提供兩種Scope,com.google.inject.Scopes.SINGLETON和com.google.inject.Scopes.NO_SCOPE,所謂沒(méi)有scope即是每次生成一個(gè)新的實(shí)例。
對(duì)于自動(dòng)注入就非常簡(jiǎn)單了,只需要在實(shí)現(xiàn)類加一個(gè)Singleton注解即可。
1 @Singleton
2 public class HelloWorldImpl implements HelloWorld {
3
4 @Override
5 public String sayHello() {
6 return "Hello, world!";
7 }
8 }
9
附:【前沿】本教程的依賴注入部分基于老菜鳥(niǎo)叮咚的教程,原文在此
http://www.family168.com/tutorial/guice/html/。原文主要基于Google Guice 1.0版本的,本文基于Google Guice 2.0版本進(jìn)行學(xué)習(xí)和討論。
下一篇:
Google Guice 入門教程02 - 依賴注入(2)©2009-2014 IMXYLZ
|求賢若渴