超輕量級DI容器框架Google Guice與Spring框架的區別教程詳解及其demo代碼片段分享

原文:超輕量級DI容器框架Google Guice與Spring框架的區別教程詳解及其demo代碼片段分享

源代碼下載地址:http://www.zuidaima.com/share/1759689106541568.htm


依賴注入,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
使用XML 使用將類與類之間的關係隔離到xml中,由容器負責注入被調用的對象,因此叫做依賴注入 不使用xml,將類與類之間的關係隔離到Module中,聲名何處需要注入,由容器根據Module裏的描述,注入被調用的對象。
使用Annotation   使用
支持自定義Annotation標註,對於相同的接口定義的對象引用,爲它們標註上不同的自定義Annotation註釋,就可以達到同一個類裏邊的同一個接口的引用,注射給不同的實現,在Module裏用標註做區分,靈活性大大增加。
使用Annotation也未必是好事,範型等新特性也未必是好事,目前大多的服務器均不支持jdk1.5,wls要9以前才支持,而目前的客戶由於價格原因也很少選用wls9的,至少我們做過的項目中都沒有。功能再強,客戶不需要,何用?
運行效率 裝載spring配置文件時,需解析xml,效率低,getBean效率也不高,不過使用環境不會涉及到getBean,只有生產環境的時候會用到getBean,在裝載spring應用程序的時候,已經完成全部的注射,所以這個低效率的問題不是問題。 使用Annotation,cglib, 效率高與spring最明顯的一個區別,spring是在裝載spring配置文件的時候把該注入的地方都注入完,而Guice呢,則是在使用的時候去注射,運行效率和靈活性高。
類耦合度 耦合度低,強調類非侵入,以外部化的方式處理依賴關係,類裏邊是很乾淨的,在配置文件裏做文章,對類的依賴性極低。 高,代碼級的標註,DI標記@inject侵入代碼中,耦合到了類層面上來,何止侵入,簡直侵略,代碼耦合了過多guice的東西,大大背離了依賴注入的初衷,對於代碼的可維護性,可讀性均不利
類編寫時 需要編寫xml,配置Bean,配置注入 只需聲明爲@inject,等着被注入,
最後在統一的Module裏聲明注入方式
僅支持IOC 否,spring目前已經涉獵很多部分 是,目前僅僅是個DI容器
是否易於代碼重構 統一的xml配置入口,更改容易 配置工作是在Module裏進行,和spring異曲同功
支持多種注入方式 構造器,setter方法 Field,構造器,setter方法
靈活性  

1,如果同一個接口定義的引用需要注入不同的實現,就要編寫不同的Module,煩瑣

2,動態注入

如果你想注射的一個實現,你還未知呢,怎麼辦呢,spring是沒辦法,事先在配置文件裏寫死的,而Guice就可以做到,就是說我想注射的這個對象我還不知道注射給誰呢,是在運行時才能得到的的這個接口的實現,所以這就大大提高了依賴注射的靈活性,動態注射。

與現有框架集成度 1, 高,衆多現有優秀的框架(如struts1.x等)均提供了spring的集成入口,而且spring已經不僅僅是依賴注入,包括衆多方面。
2, Spring也提供了對Hibernate等的集成,可大大簡化開發難度。
3, 提供對於orm,rmi,webservice等等接口衆多,體系龐大。
1,可以與現有框架集成,不過僅僅依靠一個效率稍高的DI,就想取代spring的地位,有點難度。
配置複雜度 在xml中定位類與類之間的關係,難度低 代碼級定位類與類之間的關係,難度稍高

 

 再借斧子的例子說一說spring與guice的區別
看下邊對於不同社會形態下一個人(java對象,調用者)需要一把斧子(java對象,被調用者)的例子:
(1),原始社會時,勞動社會基本沒有分工,需要斧子的人(調用者)只好自己去磨一把斧子,每個人擁有自己的斧子,如果把大家的石斧改爲鐵斧,需要每個人都要學會磨鐵斧的本領,工作效率極低。
對應Java裏的情形是:java程序裏的調用者new一個被調用者的實例。類耦合度極高,修改維護煩瑣,效率極低。
(2),工業社會時,工廠出現,斧子不再由普通人完成,而由工廠生產,當人們需要斧子的時候,可以到工廠購買斧子,無需關心斧子是怎麼製造出來的,如果廢棄鐵斧爲鋼斧,只需改變工廠的製造工藝即可,製作工藝是工廠決定的,工廠生產什麼斧子,工人們就得用什麼斧子。
 對應的Java裏的情形是:Java程序的調用者可以以來簡單工廠創建被調用者,變化點被隔離到了簡單工廠裏,雖然耦合度降低,但是調用者會和工廠耦合,而且需要定位自己的工廠。
(3)近代工業社會,工廠蓬勃發展,人們需要什麼斧子,只需要提供一個斧子圖形,商家會按照你提供的圖形將你的斧子訂做好,送上門。
對應Java裏的情形:spring的依賴注入
(4)進入按需要分配社會,信息進入現代化,人們不再去工廠購買斧子,不再拘泥於需要什麼斧子事先畫好什麼樣的圖形,只需要打個電話,描述一下需要什麼類型的斧子,或許想打造一個物美價廉的斧子,商家會根據市場零件的價格,計算出最優製作工藝,打造最適合的斧子送過來,更加信息化,更加人性化。
 對應Java裏的情形:基於描述的注入,動態的,靈活簡單的注入,如:Guice。
 
對於該不該使用Guice,我想也是仁者見仁,智者見智,就象好多論壇裏動不動有人會在那裏討論到底學Java還是學.net或者是使用eclipse還是Jbuilder的這類無聊話題,適合和滿足項目需求的,又能省工省力簡單的完成工作的,就是最好的。

在此拋磚引玉,大家有異議的地方歡迎和我討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章