Google Guice 入門教程01 - 依賴注入

openkk 13年前發布 | 4K 次閱讀 OpenJDK

【前沿】本教程基于老菜鳥叮咚的教程,原文在此http://www.family168.com/tutorial/guice/html/。原文主要基于Google Guice 1.0版本的,本文基于Google Guice 2.0版本進行學習和討論。

類依賴注入

所謂的綁定就是將一個接口綁定到具體的類中,這樣客戶端不用關心具體的實現,而只需要獲取相應的接口完成其服務即可。

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<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),實現類可以不經過綁定就獲取么?比如我想獲取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
 本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!