xylz,imxylz

          關注后端架構、中間件、分布式和并發編程

             :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks


          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<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
          附:【前沿】本教程的依賴注入部分基于老菜鳥叮咚的教程,原文在此http://www.family168.com/tutorial/guice/html/。原文主要基于Google Guice 1.0版本的,本文基于Google Guice 2.0版本進行學習和討論。

          下一篇:Google Guice 入門教程02 - 依賴注入(2)


          ©2009-2014 IMXYLZ |求賢若渴
          posted on 2009-12-22 23:28 imxylz 閱讀(35762) 評論(5)  編輯  收藏 所屬分類: J2EEGoogle Guice

          評論

          # re: Google Guice 入門教程01 - 依賴注入 2009-12-23 09:49 字典
          不錯  回復  更多評論
            

          # re: Google Guice 入門教程01 - 依賴注入(1)[未登錄] 2010-07-01 09:57 Sam
          Very Nice!  回復  更多評論
            

          # re: Google Guice 入門教程01 - 依賴注入(1) 2011-06-15 23:32 RunCode
          不錯  回復  更多評論
            

          # re: Google Guice 入門教程01 - 依賴注入(1) 2012-07-25 19:00 怒破
          博主小哥,你認識張民松嗎  回復  更多評論
            

          # re: Google Guice 入門教程01 - 依賴注入(1)[未登錄] 2016-03-05 14:12 yong
          Guice的實現方式不太優雅,在配置的繼承重用和annotation侵入性上有問題,我最近做了一個小項目叫jBeanBox (不能發鏈接,請Google之) 。特點:1.只用單個Java文件350行源碼實現完整IOC/AOP 2.用Java類代替XML作為配置,支持IDE重構,無侵入性(沒用annotation),敬請評價。  回復  更多評論
            


          ©2009-2014 IMXYLZ
          主站蜘蛛池模板: 上蔡县| 闻喜县| 克拉玛依市| 夏津县| 雅江县| 会同县| 永昌县| 定南县| 罗甸县| 城固县| 余姚市| 开封县| 遂溪县| 清原| 花莲市| 重庆市| 宝兴县| 桂阳县| 寿宁县| 多伦县| 广灵县| 西宁市| 大关县| 天等县| 兰坪| 台安县| 门源| 黑水县| 汕头市| 长泰县| 香河县| 鄂州市| 临汾市| 云南省| 明光市| 昌都县| 通化县| 吴川市| 文昌市| 聊城市| 金乡县|