依賴注入,DI(Dependency Injection),它的作用自然不必多說,提及DI容器,例如spring,picoContainer,EJB容器等等,近日,google誕生了更輕巧的DI容器……Guice!
廢話不多講了,先看看Guice是如何實現注入的吧。
定義一個簡單的service接口和它的實現吧:
package com.zuidaima.demo.guice; public interface MyService ... { void myMethod(); }
package com.zuidaima.demo.guice; public class MyServiceImpl implements MyService ... { public void myMethod() ...{ System.out.println("Hello,World!"); } }
以上是最普通的接口和其實現,沒什麼可說的。
定義一個測試類,這個類裡邊包括service對象的一個引用,這個對象是需要Guice進行注入的
package com.zuidaima.demo.guice; import com.google.inject.Inject; public class Client ... { MyService service; @Inject //告訴容器,這裡的service對象的引用,需要進行注入 void setService(MyService service) ...{ //這裡的方法名字可以任意定義 this.service=service; } public void myMethod() ...{ service.myMethod(); } }
這裡除了加了一個@Inject,和Spring的配置沒有任何的區別,@Inject,是表示對容器說,這裡的service需要注射,等到運行的時候,容器會拿來一個實例給service,完成注射的過程。
定義Guice的Module文件 告訴容器如何進行注入
package com.zuidaima.demo.guice; import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; public class MyModule implements Module ... { public void configure(Binder binder) ...{ binder.bind(MyService.class).to(MyServiceImpl.class).in(Scopes.SINGLETON); // 這句代碼的意思是說:運行時動態的將MyServiceImpl對象賦給MyService定義的對象,而且這個對象是單例的。 } }
創建測試類
package com.zuidaima.demo.guice; import com.google.inject.Guice; import com.google.inject.Injector; public class Test ... { public static void main(String[] args) ...{ MyModule module=new MyModule();// 定義注射規則 Injector injector=Guice.createInjector(module);// 根據注射規則,生成注射者 Client client=new Client(); injector.injectMembers(client);// 注射者將需要注射的bean,按照規則,把client這個客戶端進行注射 client.myMethod(); } }
運行測試類,控制台輸出:Hello,World!
完成注入過程
下面看看Guice還有哪些其它的使用特性。
1,如果在實現你確定MyService定義的對象,就要被注射為MyServiceImpl而不是其它的實現類的話,可以在MyService接口加上@ImplementedBy(MyServiceImpl.class)
package com.zuidaima.demo.guice; import com.google.inject.ImplementedBy; @ImplementedBy(MyServiceImpl. class ) // 我總覺得這樣有點背離了依賴注入的初衷了 public interface MyService ... { void myMethod(); }
這樣的話,在MyModule裡的configure方法中就可以不加任何東西,容器就會自動注射給MyServiceImpl對象。
2,可以對Field進行注解式注入
在Client.java中也可以把這個@Inject標注在MyService service;的前邊,如:@Inject MyService service;
3,可使用自定義Annotation標注。
package com.zuidaima.demo.guice; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.google.inject.BindingAnnotation; @Retention(RetentionPolicy.RUNTIME) @Target( ... { ElementType.FIELD, ElementType.PARAMETER }) @BindingAnnotation public @ interface MyInterface ... { }
那麼Client.java需要改為
package com.zuidaima.demo.guice; import com.google.inject.Inject; public class Client ... { @Inject @MyInterface MyService service; void setService(MyService service) ...{ // 這裡的方法名字可以任意定義 this.service=service; } public void myMethod() ...{ service.myMethod(); } }
MyModule.java中的configure方法內容需改為:
binder.bind(MyService.class).annotatedWith(MyInterface.class).to(
MyServiceImpl.class).in(Scopes.SINGLETON);
意思是說對於標注為MyInterface的MyService定義的對象進行注入
進行Annotation標注的成員(Field,method,argument等)進行自定義Annotation標注,該成員既擁有該屬性,可以在運行,根據這些成員的不同屬性,做一些不同的事情 例如:spring的AspectJ,xdoclet等都是如此。
下邊是我做了一下對比
Guice與Spring的對比
再借斧子的例子說一說spring與guice的區別
看下邊對於不同社會形態下一個人(java對象,調用者)需要一把斧子(java對象,被調用者)的例子:
(1),原始社會時,勞動社會基本沒有分工,需要斧子的人(調用者)只好自己去磨一把斧子,每個人擁有自己的斧子,如果把大家的石斧改為鐵斧,需要每個人都要學會磨鐵斧的本領,工作效率極低。
對應Java裡的情形是:java程序裡的調用者new一個被調用者的實例。類耦合度極高,修改維護煩瑣,效率極低。
(2),工業社會時,工廠出現,斧子不再由普通人完成,而由工廠生產,當人們需要斧子的時候,可以到工廠購買斧子,無需關心斧子是怎麼制造出來的,如果廢棄鐵斧為鋼斧,只需改變工廠的制造工藝即可,制作工藝是工廠決定的,工廠生產什麼斧子,工人們就得用什麼斧子。
對應的Java裡的情形是:Java程序的調用者可以以來簡單工廠創建被調用者,變化點被隔離到了簡單工廠裡,雖然耦合度降低,但是調用者會和工廠耦合,而且需要定位自己的工廠。
(3)近代工業社會,工廠蓬勃發展,人們需要什麼斧子,只需要提供一個斧子圖形,商家會按照你提供的圖形將你的斧子訂做好,送上門。
對應Java裡的情形:spring的依賴注入
(4)進入按需要分配社會,信息進入現代化,人們不再去工廠購買斧子,不再拘泥於需要什麼斧子事先畫好什麼樣的圖形,只需要打個電話,描述一下需要什麼類型的斧子,或許想打造一個物美價廉的斧子,商家會根據市場零件的價格,計算出最優制作工藝,打造最適合的斧子送過來,更加信息化,更加人性化。
對應Java裡的情形:基於描述的注入,動態的,靈活簡單的注入,如:Guice。
對於該不該使用Guice,我想也是仁者見仁,智者見智,就象好多論壇裡動不動有人會在那裡討論到底學Java還是學.net或者是使用eclipse還是Jbuilder的這類無聊話題,適合和滿足項目需求的,又能省工省力簡單的完成工作的,就是最好的。
在此拋磚引玉,大家有異議的地方歡迎和我討論。
代碼下載地址:http://www.zuidaima.com/share/1759689106541568.htm