1. 依賴注入
1.1 類依賴注入
所謂的綁定就是將一個接口綁定到具體的類中,這樣客戶端不用關心具體的實現(xiàn),而只需要獲取相應的接口完成其服務即可。
HelloWorld.java
1 public interface HelloWorld {
2
3 String sayHello();
4 }
5
然后是具體的實現(xiàn),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關聯(lián)起來,當想Guice獲取一個HelloWorld實例的時候,Guice就返回一個HelloWorldImpl的實例,然后我們就可以調(diào)用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),如果綁定多個實現(xiàn)到同一個接口上會出現(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目前看起來不允許多個實例綁定到同一個接口上了。
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),可以綁定一個實現(xiàn)類到實現(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)
我們來看看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());
太好了,支持子類綁定,這樣即使我們將一個實現(xiàn)類發(fā)布出去了(盡管不推薦這么做),我們在后期仍然有辦法替換實現(xiàn)類。
使用bind有一個好處,由于JAVA 5以上的泛型在編譯器就確定了,所以可以幫我們檢測出綁定錯誤的問題,而這個在配置文件中是無法檢測出來的。
這樣看起來Module像是一個Map,根據(jù)一個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<T>),允許自己提供構造對象的方式。
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),實現(xiàn)類可以不經(jīng)過綁定就獲取么?比如我想獲取HelloWorldImpl的實例而不通過Module綁定么?
解答(7),可以,實際上Guice能夠自動尋找實現(xiàn)類。
Injector inj= Guice.createInjector();
HelloWorld hw = inj.getInstance(HelloWorldImpl.class);
System.out.println(hw.sayHello());
問題(8),可以使用注解方式完成注入么?不想手動關聯(lián)實現(xiàn)類。
解答(8),好,Guice提供了注解的方式完成關聯(lián)。我們需要在接口上指明此接口被哪個實現(xià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());
事實上對于一個已經(jīng)被注解的接口我們?nèi)匀豢梢允褂肕odule來關聯(lián),這樣獲取的實例將是Module關聯(lián)的實例,而不是@ImplementedBy注解關聯(lián)的實例。這樣仍然遵循一個原則,手動優(yōu)于自動。
問題(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
可以看到現(xiàn)在獲取的實例已經(jīng)是單例的,不再每次請求生成一個新的實例。事實上Guice提供兩種Scope,com.google.inject.Scopes.SINGLETON和com.google.inject.Scopes.NO_SCOPE,所謂沒有scope即是每次生成一個新的實例。
對于自動注入就非常簡單了,只需要在實現(xiàn)類加一個Singleton注解即可。
1 @Singleton
2 public class HelloWorldImpl implements HelloWorld {
3
4 @Override
5 public String sayHello() {
6 return "Hello, world!";
7 }
8 }
9
附:【前沿】本教程的依賴注入部分基于老菜鳥叮咚的教程,原文在此
http://www.family168.com/tutorial/guice/html/。原文主要基于Google Guice 1.0版本的,本文基于Google Guice 2.0版本進行學習和討論。
下一篇:
Google Guice 入門教程02 - 依賴注入(2)©2009-2014 IMXYLZ
|求賢若渴