struts負責控制Service(業務邏輯處理類),從而控制了Service的生命週期,這樣層與層之間的依賴很強,屬於耦合。這時,使用spring框架就起到了控制Action物件(Strus中的)和Service類的作用,兩者之間的關係就鬆散了,Spring的Ioc機制(控制反轉和依賴注入)正是用在此處。
1. Spring的Ioc(控制反轉和依賴注入)
控制反轉:就是由容器控制程式之間的(依賴)關係,而非傳統實現中,由程式程式碼直接操控
依賴注入:元件之間的依賴關係由容器在執行期決定 ,由容器動態的將某種依賴關係注入到元件之中 。
從上面我們不難看出:從頭到尾Action僅僅是充當了Service的控制工具,這些具體的業務方法是怎樣實現的,他根本就不會管,也不會問,他只要知道這些業務實現類所提供的方法介面就可以了。而在以往單獨使用Struts框架的時候,所有的業務方法類的生命週期,甚至是一些業務流程都是由Action來控制的。層與層之間耦合性太緊密了,既降低了資料訪問的效率又使業務邏輯看起來很複雜,程式碼量也很多。,Spring容器控制所有Action物件和業務邏輯類的生命週期,由於上層不再控制下層的生命週期,層與層之間實現了完全脫耦,使程式執行起來效率更高,維護起來也方便。
2. 使用Spring的第二個好處(AOP應用):
事務的處理:
在以往的JDBCTemplate中事務提交成功,異常處理都是通過Try/Catch 來完成,而在Spring中。Spring容器整合了TransactionTemplate,她封裝了所有對事務處理的功能,包括異常時事務回滾,操作成功時資料提交等複雜業務功能。這都是由Spring容器來管理,大大減少了程式設計師的程式碼量,也對事務有了很好的管理控制。Hibernate中也有對事務的管理,hibernate中事務管理是通過SessionFactory建立和維護Session來完成。而Spring對SessionFactory配置也進行了整合,不需要在通過hibernate.cfg.xml來對SessionaFactory進行設定。這樣的話就可以很好的利用Sping對事務管理強大功能。避免了每次對資料操作都要現獲得Session例項來啟動事務/提交/回滾事務還有繁瑣的Try/Catch操作。這些也就是Spring中的AOP(面向切面程式設計)機制很好的應用。一方面使開發業務邏輯更清晰、專業分工更加容易進行。另一方面就是應用Spirng AOP隔離降低了程式的耦合性使我們可以在不同的應用中將各個切面結合起來使用大大提高了程式碼重用度
Spring Framework(簡稱Spring)是根據Rod Johnson著名的《Expert One-on-One J2EE Design and Development》而開發的J2EE應用程式框架。目前主要根據Rod Johnson和Juergen Hoeller而進行開發的,目前釋出的最新版為1.1.4。 Spring是J2EE應用程式框架,不過,更嚴格地講它是針對Bean的生命週期進行管理的輕量級容器(Lightweight container),可以單獨利用Spring構築應用程式,也可以和Struts,Webwork,Tapestry等眾多Web應用程式框架組合使用,並且可以與Swing等桌面應用程式API組合。所以Spring並不僅僅只能應用在J2EE中,也可以應用在桌面應用及小應用程式中。針對Spring開發的元件不需要任何外部庫。
2.使用Spring有什麼好處?
(1)Spring能有效地組織你的中間層物件。
(2)Spring能消除在許多工程中常見的對Singleton的過多使用。
(3)Spring能消除各種各樣自定義格式的屬性檔案的需要,使配置資訊一元化。
(4)Spring能夠幫助我們真正意義上實現針對介面程式設計。
(5)在Spring應用中的大多數業務物件沒有依賴於Spring。
(6)使用Spring構建的應用程式易於單元測試。
(7)Spring支援JDBC和O/R Mapping產品(Hibernate)
(8)MVC Web框架,提供一種清晰,無侵略性的MVC實現方式。
(9)JNDI抽象層,便於改變實現細節,可以方便地在遠端服務和本地服務間切換。
(10)簡化訪問資料庫時的例外處理。
(11)Spring能使用AOP提供宣告性事務管理,可以不直接操作JTA也能夠對事務進行管理。
(12)提供了JavaMail或其他郵件系統的支援。
1.降低了元件之間的耦合性 ,實現了軟體各層之間的解耦
2.可以使用容易提供的眾多服務,如事務管理,訊息服務等
3.容器提供單例模式支援
4.容器提供了AOP技術,利用它很容易實現如許可權攔截,執行期監控等功能
5.容器提供了眾多的輔助類,能加快應用的開發
6.spring對於主流的應用框架提供了整合支援,如hibernate,JPA,Struts等
7.spring屬於低侵入式設計,程式碼的汙染極低
8.獨立於各種應用伺服器
9.spring的DI機制降低了業務物件替換的複雜性
10.Spring的高度開放性,並不強制應用完全依賴於Spring,開發者可以自由選擇spring的部分或全部
3.什麼是輕量(Lightweight)級容器?
Spring的開發者可以避免使用重量級容器開發EJB時的缺點:
(1)帶有侵略性的API。(程式碼依賴於EJB)
(2)對容器的依賴。(程式碼不能在EJB容器之外工作)
(3)提供固定的一組機能,不具有配置能力。
(4)不同的產品,部署過程不同,不易通用。
(5)啟動時間長。
針對以上問題,Spring採用了IoC使程式碼對Spring的依賴減少,根據Web應用,小應用程式,桌面應用程的不同,對容器的依賴程度也不同。Spring將管理的Bean作為POJO(Plain Old Java Object)進行控制,通過AOP Interceptor能夠增加其它的功能。
除了Spring以外的輕量級容器還有PicoContainer,(不是輕量級容器)對Bean的生命週期進行管理的還有Apache Avalon Project的Avalon等。
總結:Spring的核心思想便是IoC和AOP,Spring本身是一個輕量級容器,和EJB容器不同,Spring的元件就是普通的Java Bean,這使得單元測試可以不再依賴容器,編寫更加容易。Spring負責管理所有的Java Bean元件,同樣支援宣告式的事務管理。我們只需要編寫好Java Bean元件,然後將它們"裝配"起來就可以了,元件的初始化和管理均由Spring完成,只需在配置檔案中宣告即可。這種方式最大的優點是各元件的耦合極為鬆散,並且無需我們自己實現Singleton模式。
下面我們來看看Spring中的單例實現,當我們試圖從Spring容器中取得某個類的例項時,預設情況下,Spring會才用單例模式進行建立。
<bean id="date" class="java.util.Date"/>
<bean id="date" class="java.util.Date" scope="singleton"/> (僅為Spring2.0支援)
<bean id="date" class="java.util.Date" singleton="true"/>
以上三種建立物件的方式是完全相同的,容器都會向客戶返回Date類的單例引用。那麼如果我不想使用預設的單例模式,每次請求我都希望獲得一個新的物件怎麼辦呢?很簡單,將scope屬性值設定為prototype(原型)就可以了
<bean id="date" class="java.util.Date" scope="prototype"/>
通過以上配置資訊,Spring就會每次給客戶端返回一個新的物件例項。
那麼Spring對單例的底層實現,到底是餓漢式單例還是懶漢式單例呢?呵呵,都不是。
Spring框架對單例的支援是採用單例登錄檔的方式進行實現的,原始碼如下:
public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{
/**
* 充當了Bean例項的快取,實現方式和單例登錄檔相同
*/
private final Map singletonCache=new HashMap();
public Object getBean(String name)throws BeansException{
return getBean(name,null,null);
}
...
public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{
//對傳入的Bean name稍做處理,防止傳入的Bean name名有非法字元(或則做轉碼)
String beanName=transformedBeanName(name);
Object bean=null;
//手工檢測單例登錄檔
Object sharedInstance=null;
//使用了程式碼鎖定同步塊,原理和同步方法相似,但是這種寫法效率更高
synchronized(this.singletonCache){
sharedInstance=this.singletonCache.get(beanName);
}
if(sharedInstance!=null){
...
//返回合適的快取Bean例項
bean=getObjectForSharedInstance(name,sharedInstance);
}else{
...
//取得Bean的定義
RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);
...
//根據Bean定義判斷,此判斷依據通常來自於元件配置檔案的單例屬性開關
//<bean id="date" class="java.util.Date" scope="singleton"/>
//如果是單例,做如下處理
if(mergedBeanDefinition.isSingleton()){
synchronized(this.singletonCache){
//再次檢測單例登錄檔
sharedInstance=this.singletonCache.get(beanName);
if(sharedInstance==null){
...
try {
//真正建立Bean例項
sharedInstance=createBean(beanName,mergedBeanDefinition,args);
//向單例登錄檔註冊Bean例項
addSingleton(beanName,sharedInstance);
}
catch (Exception ex) {
...
}
finally{
...
}
}
}
bean=getObjectForSharedInstance(name,sharedInstance);
}
//如果是非單例,即prototpye,每次都要新建立一個Bean例項
//<bean id="date" class="java.util.Date" scope="prototype"/>
else{
bean=createBean(beanName,mergedBeanDefinition,args);
}
}
...
return bean;
}
}
以上的原始碼對於很多同學來說,可能感覺比較恐怖,但是學習要學會抓住要領,剛才的原始碼中,大家真正要記住的是Spring對bean例項的建立是採用單例登錄檔的方式進行實現的,而這個登錄檔的快取是HashMap物件,如果配置檔案中的配置資訊不要求使用單例,Spring會採用新建例項的方式返回物件例項
- singleton---單例模式
每個bean定義只生成一個物件例項,每次getBean請求獲得的都是此例項
- 單例模式分為餓漢模式和懶漢模式
餓漢模式 | spring singleton的預設是餓漢模式:啟動容器時(即例項化容器時),為所有spring配置檔案中定義的bean都生成一個例項 |
懶漢模式 | 在第一個請求時才生成一個例項,以後的請求都呼叫這個例項 spring singleton設定為懶漢模式: <beans default-lazy-init="true"> |
- 另一種和singleton對應的scope值---prototype多例項模式
- singleton和prototype的比較
xml配置檔案: <bean id="dvdTypeDAO" class="com.machome.hibernate.impl.DvdTypeDAOImpl" /> |
測試程式碼: ctx = new ClassPathXmlApplicationContext("spring-hibernate-mysql.xml"); DvdTypeDAO tDao1 = (DvdTypeDAO)ctx.getBean("dvdTypeDAO"); DvdTypeDAO tDao2 = (DvdTypeDAO)ctx.getBean("dvdTypeDAO"); |
執行: true com.machome.hibernate.impl.DvdTypeDAOImpl@15b0333 com.machome.hibernate.impl.DvdTypeDAOImpl@15b0333 說明前後兩次getBean()獲得的是同一例項,說明spring預設是單例 |
prototype
<bean id="dvdTypeDAO" class="com.machome.hibernate.impl.DvdTypeDAOImpl" scope="prototype" /> |
執行同樣的測試程式碼 |
執行: false com.machome.hibernate.impl.DvdTypeDAOImpl@afae4a com.machome.hibernate.impl.DvdTypeDAOImpl@1db9852 說明scope="prototype"後,每次getBean()的都是不同的新例項 |
- singleton---單例模式
每個bean定義只生成一個物件例項,每次getBean請求獲得的都是此例項
- 單例模式分為餓漢模式和懶漢模式
餓漢模式 | spring singleton的預設是餓漢模式:啟動容器時(即例項化容器時),為所有spring配置檔案中定義的bean都生成一個例項 |
懶漢模式 | 在第一個請求時才生成一個例項,以後的請求都呼叫這個例項 spring singleton設定為懶漢模式: <beans default-lazy-init="true"> |
- 另一種和singleton對應的scope值---prototype多例項模式
- singleton和prototype的比較
xml配置檔案: <bean id="dvdTypeDAO" class="com.machome.hibernate.impl.DvdTypeDAOImpl" /> |
測試程式碼: ctx = new ClassPathXmlApplicationContext("spring-hibernate-mysql.xml"); DvdTypeDAO tDao1 = (DvdTypeDAO)ctx.getBean("dvdTypeDAO"); DvdTypeDAO tDao2 = (DvdTypeDAO)ctx.getBean("dvdTypeDAO"); |
執行: true com.machome.hibernate.impl.DvdTypeDAOImpl@15b0333 com.machome.hibernate.impl.DvdTypeDAOImpl@15b0333 說明前後兩次getBean()獲得的是同一例項,說明spring預設是單例 |
prototype
<bean id="dvdTypeDAO" class="com.machome.hibernate.impl.DvdTypeDAOImpl" scope="prototype" /> |
執行同樣的測試程式碼 |
執行: false com.machome.hibernate.impl.DvdTypeDAOImpl@afae4a com.machome.hibernate.impl.DvdTypeDAOImpl@1db9852 說明scope="prototype"後,每次getBean()的都是不同的新例項 |