摘要
J2EE編程正在變得越來越復雜。J2EE已經發展為一個API、復雜化的編程和配置的復雜網絡。為了應對這種復雜性,新的框架和方法不斷涌現。這些框架高度依賴于一個稱為IoC(Inversion of Control,反向控制)的概念。本文將探討這種方法的一些特性和優點,因為這種方法與J2EE編程相關,而且可以使J2EE編程變得更輕松。
簡介
馬克·吐溫的一句話常被引用:“……關于我死亡的報道是一種夸張?!爆F在已經出現了很多關于.Net的流言,以及認為J2EE API的復雜性無法克服和EJB作為一種組件架構即將滅亡的流行極客(geek)文化。從學術或者只是想像的立場來看,這沒什么大不了的,但事實是J2EE/EJB API已經經歷了一場達爾文式的進化。具有DCOM或CORBA項目經驗的讀者會明白我的意思。過去,人們都樂于聽聞EJB組件模型的美好前景。實際情況是,人們在與J2EE相關的各個方面都投入巨大。宣布拋棄以前的所有工作并重新組織,這種想法看起來也許有理,但是它并沒有建立在良好的業務洞察力之上。EJB繼續發展,而術語、實踐和框架也隨之涌現(sPRing up),它們彌補了J2EE API的不足。我說的不是“Spring出現(up)”,對吧?
我是一名顧問,職責是幫助構建大型的分布式應用程序,而且通常是J2EE應用程序。因此,我有機會親歷許多項目的整個生命周期。另外我還能夠將我從一個剛剛完成的項目中剛剛學到的東西直接帶入一個全新的項目。從某種意義上說我的“自然選擇”過程加快了。我可以說最近Spring(更具體地說就是IoC,即反向控制)已經越來越多地融入到我的項目中了。在本文中,我將從支持或增強J2EE項目的角度來探討Spring。更確切地講,Spring框架能夠標準化許多J2EE最佳實踐,還能同類化(homogenize)許多無處不在的J2EE模式。接下來我們將瀏覽Spring龐大體系中的一小部分內容,重點介紹(依我淺見)能夠幫助改進J2EE應用程序的功能。
IoC簡介
一般來說,IoC是一種管理類之間關聯的技術。沒錯,就這么簡單!任何人都不是孤立的,對于各個對象來說也是如此。應用程序中的對象是相互依賴的。通過編程方式來表現這種依賴性通常既冗長又容易出錯。好的IoC框架將聲明式地(通過一個xml配置文件)而不是編程式地(這種方式的可靠性較差)——串連起應用程序之間的相互依賴性。
自由使用接口是IoC開發的一個主要方針。接口編程大大提高了應用程序的靈活性,從而增強了聲明式的關聯。接口實現是通過IoC配置在運行時聲明的,這樣就能夠在不影響或少影響實際應用程序代碼的情況下“重建(rewire)”關聯。這在各種IoC框架中是反復提及的一個主題,一般而言,也是應該遵循的良好實踐。
一個小例子
我喜歡通過例子來更快地理解概念。下面就是運用了IoC的一組例子;您將看到,這些例子的復雜性是逐遞增的。大多數人在一開始使用IoC容器時都是利用其依賴注入(inject dependency)功能——即,聲明式地將對象關聯起來。利用IoC有助于創建更整潔的代碼,如有必要重建對象之間的關聯,一般來說對于這些代碼也會更靈活、更容易。IoC的優點遠不止依賴注入,而其擴展功能確是以依賴注入程序為起點的。
我們將從構建簡單的依賴注入例子開始。第一個例子用于闡明已經提及的兩個概念。第一個概念是IoC在運行時構建和關聯對象的能力,第二個是與接口編碼相結合而產生的靈活性。首先假定架構師遞交了圖1所示的UML。
圖1. 接口可插性
這個小例子表示一個溫度測量系統。幾個傳感器對象屬于不同的類型,但都實現了ProtocolAdapterIfc接口,因此在將它們插入TemperatureSensor對象時,它們是可互換的。在需要TemperatureSensor時,系統中的某個實體必須知道要生成并與該傳感器對象關聯的ProtocolAdapterIfc的具體類型。在本例中,該傳感器可基于命令行參數、數據庫中的行或通過屬性文件進行配置。本例還不足以造成挑戰或展示一個復雜框架,但它足以闡明IoC基礎。
但是,想象一下:在一個相當復雜的應用程序中這種情況屢屢發生,而您還希望能動態地——至少要在外部——改變對象關聯。假設有一個DummyProtocolAdapter,它總是返回42這個值,使用它來進行測試。為什么不提供一個單個的統一框架?——讓開發人員能夠依靠該框架,以一種一致的、外部配置的方式建立類之間的關聯,并且不引起工廠單元素類(factory singleton classe)的異常增加。這聽起來可能沒什么大不了,但它要依賴于IoC的簡單性。
我們使用一個TemperatureSensor類,它與一個實現ProtocolAdapterIfc接口的類有關聯。TemperatureSensor將使用該委托類來獲得溫度值。如UML圖所示,在實現ProtocolAdapterIfc并且隨后可用于該關聯的應用程序中有若干個類。我們將使用IoC框架(在本例中是Spring)來聲明要使用的ProtocolAdaperIfc的實現。Spring將在運行時建立關聯。我們先來看XML代碼,它將實例化TemperatureSensor對象并將一個ProtocolAdapterIfc實現與它關聯起來。該代碼如下所示:<bean id="tempSensor"class="yourco.project.sensor.TemperatureSensor"> <property name="sensorDelegate"> <ref bean="sensor"/> </property></bean><!-- Sensor to associate with tempSensor --><bean id="sensor" class="yourco.project.comm.RS232Adapter"/>
看了這些代碼之后,對于其目的就應該非常清楚了。我們配置Spring來實例化TemperatureSensor對象,并將其與RS232Adapter相關聯,作為實現ProtocolAdapterIfc接口的類。若想改變已經與TemperatureSensor關聯的實現,惟一需要更改的就是sensor bean標記中的class值。只要實現了ProtocolAdapterIfc接口,TemperatureSensor就不再關心關聯了什么。
將這應用于應用程序相當簡單。我們必須先接入Spring框架,將它指向正確的配置文件,然后根據名稱向Spring索取tempSensor對象的實例。下面是相應的代碼:
ClassPathXmlapplicationContext appContext = new ClassPathXmlApplicationContext( new String[] { "simpleSensor.xml" });BeanFactory bf = (BeanFactory) appContext;TemperatureSensor ts = (TemperatureSensor) bf.getBean("tempSensor");System.out.println("The temp is: "+ ts.getTemperature());
可以看出,這些代碼并不是非常難。首先是啟動Spring并指定要使用的配置文件。接下來根據名稱(tempSensor)引用Bean。Spring使用這樣一種機制:基于simpleSensor.xml文件的描述創建該對象并與其他對象關聯。它用于注入依賴性——在本例中,通過將它作為一個參數傳遞給sensorDelegate()方法而實例化RS232Adapter對象并將其與TemperatureSensor對象關聯。
比較起來,使用編程式java完成這一任務也不是很難。如下所示:
TemperatureSensor ts2 = new TemperatureSensor();ts2.setSensorDelegate(new RS232Adapter());
純粹主義者或許會認為實際上這是更好的方法。代碼行數少,并且可讀性可能更強。確實如此,但這種方法的靈活性要小得多。
上面所述的例子可能是依賴注入的最簡單形式。利用相同的策略,我們不僅能夠關聯不同的類,還能夠在類中安裝屬性。諸如字符串、整數或浮點數之類的屬性,只要具有JavaBean樣式的存取器,就可以通過Spring配置文件將它們注入類中。我們還可以通過構造函數來創建對象和安裝屬性或bean引用。其語法只比通過屬性進行設置稍稍復雜一些。
所有這一切都是利用一種靈活的聲明性配置完成的。無需更改代碼,建立依賴關聯的所有艱難任務都由Spring來完成。
Spring--標準化的定位器模式
我一直將服務定位器模式視作良好的J2EE規范的主要組成部分。對于不熟悉這一術語的人來說,可以這樣理解它:我們一般認為典型的J2EE應用程序由若干層組成。通常有Web層、服務層(EJB、JMS、WS、WLS控件)以及數據庫。一般來說,完成某一請求所需的“查找”服務中都包含了一些方法。Service Locator(服務定位器)模式認為,將這些方法包裝在某種隱藏了生成或查找給定服務的復雜性的工廠類中是一個好主意。這減少了JNDI或只會造成Web層操作類混亂的其他服務產品代碼的增加。在Spring出現以前,這通常是由經過考驗證明可靠的(tried-and-true)Singleton類來實現的。Singleton/Locator/Factory模式可以描繪為:
圖2. 定位器模式的順序圖
這是對散布在整個Web控制器代碼中的增加的JNDI查找代碼的一個巨大改進。它被巧妙地隱藏在工廠內部的協作類中。我們可以使用Spring來改進這一術語。此外,該解決方案將適用于EJB、Web services、異步JMS調用,甚至還有基于WLS控件的服務。由Spring實現的這種定位器模式的變體考慮了業務服務之間的一些抽象化和同質性。換句話說,Web控制器的開發人員真的可以不考慮他們所使用的服務的種類,一個類似于“WLS控件”但是更通用的概念。
IoC框架大大改進了這種模式的效用,而且實際上廢除了復雜而特殊的singleton代碼來實現它。通過借用上例中引入的概念,我們實際上無需額外代碼便能構建一個非常強大且無處不在的Service Locator模式。為此,在一開始有一個簡單的要求,即Web操作的開發人員應專門處理實現接口的那些事情。這基本上已經通過EJB編程實現,但并不是說Web操作的開發人員處理的服務必須通過EJB來實現。它們可能只是普通Java對象或Web services。要點是應當通過接口(這樣實現能夠換入換出)來編寫服務程序,并且運行時配置能夠由Spring處理。
Spring之所以非常適合于Service Locator模式,是因為它或多或少能夠統一地處理不同類型的對象。通過少許的規劃和大量使用IoC,我們多少都能夠以一種通用方式來處理大多數對象,而不用管它們的特性(EJB、POJO等等)如何,并且不會引起Singleton工廠類的增加。這使Web層編程變得更加輕松和靈活。
我們先來看一個關于這種模式如何應用于EJB的例子。我們都知道使用EJB可能是最復雜的方法,因為要將一個活動的引用引入EJB要做很多工作。若使用Spring,建議用EJB接口擴展非特定于EJB的業務接口。這樣做有兩個目的:保持兩個接口自動同步,以及幫助保證業務服務對非EJB實現是可交換的,以便進行測試或清除(stubbing)。我們可以利用Spring固有的實用工具來定位和創建EJB實例,同時為我們處理所有難以處理的工作。相應代碼如下所示:<bean id="myBizServiceRef" class="org.springframework.ejb.access. LocalStatelesssessionProxyFactoryBean"> <property name="jndiName"> <value>myBizComponent</value> </property> <property name="businessInterface"> <value> yourco.project.biz.MyBizInterface </value> </property></bean>
接下來可以檢索bean并開始使用它,方法如下:
MyBizInterface myService = bf.getBean("myBizServiceRef");
這將返回Spring動態創建并包裝了底層目標(在本例中是一個本地EJB實例)的一個對象。這種方法非常好,因為它完全隱藏了我們在處理EJB這一事實。我們將與一個實現簡單業務接口的代理對象交互。Spring已經基于“真正的”業務對象考慮周到地動態生成了該對象。所包裝的對象當然就是Spring定位和檢索引用所要獲得的本地EJB。此外,您還會注意到,這種代碼形式與前面用于檢索tempSensor對象的代碼完全相同。
那么如果我們改變主意,想用普通Java對象來實現業務組件;或者可能在測試中,我們想用一個返回“固定(canned)”響應的已清除(stubbed)對象來替換重量級EJB,該怎么做呢?利用IoC和Spring,通過更改Spring上下文文件就可輕而易舉地實現這些目標。我們只需使用更常規一點的東西(如我們在第一個Spring例子中所看到的)來替換EJB代理的連接即可:
<bean id="myBizServiceRef"
class="yourco.project.biz.MyStubbedBizService">
</bean>
請注意,我只更改了Spring框架所返回的內容的細節,沒有更改bean id。最后的結果是業務對象的解決方案未變;它看上去和以前完全一樣:
MyBizInterface myService =
bf.getBean("myBizServiceRef");
最大的區別顯然是實現該業務接口的對象現在由一個普通Java對象(POJO)支持,并且只是該接口的一個已清除(stubbed)版本。這給單元測試或改變業務服務的特性帶來了極大方便,而對客戶端代碼的影響很小。
使用Spring來標準化異常
Spring的一大貢獻是“模板化”代碼塊。這在純JDBC編程中表現得最為明顯。我們都曾寫過具有下述功能的代碼:
但是各處的這種代碼往往都會或多或少地有點“樣板化”。一般來說這是有害的,不僅因為不需要的代碼會增加,還因為有些東西可能會遺漏,如非常重要的關閉連接,如果沒有實現它,可能導致數據資源池的泄漏。
雖然我敢肯定我們都曾多次寫過這類“樣板”代碼,但是將Spring方法和直接的JDBC實現對照來看,其結果將會有趣而又對比鮮明?!皞鹘y”的JDBC實現可能如下:
Connection con = null;try{ String url = "jdbc://blah.blah.blah;"; con = myDataSource().getConnection(); Statement stmt = con.createStatement(); String query = "SELECT TYPE FROM SENSORS"; ResultSet rs = stmt.executeQuery(query); while(rs.next()){ String s = rs.getString("TYPE); logger.debug(s + " " + n); }} catch(SQLException ex){ logger.error("SQL ERROR!",ex);}finally{ con.close();}
對于該方法要做一些說明。首先,它是有效的!該代碼絕對不會出現任何錯誤。它會連接到數據庫,并從‘SENSOR’表獲取所需的數據。該方法的基本問題源于缺乏抽象化。在大型應用程序中,必須反復剪切和粘貼這段代碼,或者至少會出現類似的其他情況。較大的問題在于它依賴于編程人員去做“該做的事”。我們都知道,不管數據庫操作的結果是什么,都必須用finally語句來關閉該數據庫。有時我們忘記做該做的事。我和所有人一樣感到內疚!
編寫一個小框架來解決這一問題會非常容易。我相信大家也都曾這樣做過,但為何不讓Spring幫我們處理這一問題呢?我不敢保證我能想出一個更整潔、更優雅的解決方案。我們來看Spring框架是如何處理這種樣板JDBC場景的。
Spring支持各種各樣的JDBC、Hibernate、JDO和iBatis模板。模板采用樣板概念,并將它轉換為合法的編程術語。例如,下面的代碼片斷封裝了上面列出的各個步驟:
DataSource ds = (DataSource) bf.getBean("myDataSource");JdbcTemplate temp = new JdbcTemplate(ds);List sensorList = temp.query("select sensor.type FROM sensors", new RowMapper() { public Object mapRow(ResultSet rs, int rowNum) throws SQLException; return rs.getString(1); } });
這段簡短的代碼消除了JDBC編程的冗長,代表了前面提及的樣板的思路。請注意我們使用了Spring的IoC來查找該查詢的數據源。Spring還支持對已檢查異常使用未檢查異常;因此許多已檢查的JDBC異常會重新映射到通常更有用而且更友好的未檢查異常層次結構中。在Spring的上下文文件中配置該數據源類似于下面代碼:
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>org.gjt.mm.MySQL.Driver</value> </property> <property name="url"> <value>jdbc:mysql://romulus/sensors</value> </property> <property name="username"> <value>heater</value> </property> <property name="passWord"> <value>hotshot</value> </property></bean>
在本例中,我們利用Apache commons工具箱配置了一個基本數據源。但并不是說我們只能使用它。我們可以改變配置,使用在JNDI中配置并裝載的數據源。Spring提供了一些實用工具和IoC功能以配置和返回存儲在JNDI中的對象。例如,若想配置并使用與JNDI上下文關聯的數據源,則可以輸入以下代碼,替換先前的數據源配置:
<bean id="myDataSource" class="org.springframework.jndi. JndiObjectFactoryBean"> <property name="tempSensorDS"> <value>ConnectionFactory</value> </property></bean>
該代碼突出了Spring所提供的關于表格的測試靈活性。該代碼可以在“容器內”運行(從JNDI查找數據源),經過細微的改動之后也可在“容器外”運行。
雖然模板化機制僅適用于某些特定場合,但我們可以泛化這一概念,將它應用于更廣泛的場合。例如,一種將已檢查異常轉變為未檢查異常、此外還可能為異常處理提供一些中間或統一的異常處理策略的機制將會很有幫助。我們還可以使用該機制將底層的基本異?!败浕钡酶虾跞艘?。我們可以將PacketFrameParityFaultException軟化為CommunicationsUnreliableException。這個重新映射的較軟的異常表示情況可能并不那么嚴重,重新請求也是可以的。
Spring已經具備了一種類似于包裝EJB調用(在最后一節介紹)的機制,但遺憾的是它并不具備任何“通用”的東西,至少在異常軟化意義上是這樣的。但Spring的確有一個非常健壯的AOP(面向方面編程)框架,我們可以用它來逼近這種行為。下面是一個關于這種軟化適用領域的(公認的)精心設計的例子。
我們再來看看本文前面已經開始探討的一些概念。在第一節中我們介紹了一個基于遠程傳感器的小應用程序?,F在我們繼續探討這個例子。我們將從一個簡單的傳感器接口開始介紹,該接口代碼如下:
public interface ProtocolAdapterIfc{ public Integer getRemoteSensorValue() throws CommChecksumFault, CommConnectFailure, CommPacketSequenceFault;}
這并沒有什么特別之處。顯然實現該接口的任何人都會獲得一個遠程值并將它返回給調用者。在此期間調用者可能要面對某些可怕的災難,該接口可能拋出的已檢查異常就是例證。
接下來我們來看該接口的一個實現程序。實現ProtocolAdapter的類是CarrierPigeon,其代碼類似于:
public class CarrierPigeon implements ProtocolAdapterIfc{ private boolean isTired = true; private boolean canFlapWings = false; public Integer getRemoteSensorValue() throws CommChecksumFault, CommConnectFailure, CommPacketSequenceFault { if(isTired && !canFlapWings ) { throw new CommConnectFailure("I'm Tired!"); } return new Integer(42); }}
為簡潔起見,這里省略了屬性的getter和setter方法。當調用getRemoteSensorValue()時,CarrierPigeon方法將檢查它是否使用過度以及它還能否執行。如果它確是使用過度并且不能執行,我們將無法獲得任何值,必須拋出CommConnectionFailure,它是一個已檢查異常。到現在為止,一直都還不錯。但別高興得太早了!我已經決定不讓應用程序編程人員應對“疲勞的信鴿”??梢詫⑺b在某種對象中,并在達到目的之前捕獲異常,但必須處理20種不同的傳感器。此外,我更喜歡對這些東西進行適度透明地處理,隨著對該系統了解的增多,或許還會更改異常處理程序中的邏輯。我想要的是一個兼容API,應用程序編程人員能夠使用它,并且隨著時間的推移能夠改變或增強。我想模板化異常處理,并讓應用程序編程人員能夠處理軟的未檢查異常,而不是硬異常。Spring非常符合這種情況。下面是為此而使用的策略的要點:
首先來看這個較軟的接口:
public interface SensorIfc{ public Integer getSensorValue();}
請注意,在限定范圍內可以重命名方法,使其更有意義。還可以消除已檢查異常,就像這里所做的一樣。接下來,也可能會更有趣的是我們想要讓Spring將其注入調用棧的攔截器:
import org.aopalliance.intercept.MethodInterceptor;public class SensorInvocationInterceptor implements MethodInterceptor{ public Object invoke(MethodInvocation invocationTarget) throws Throwable { // Return object reference Object o = null; // Convert it to the protocol interface type ProtocolAdapterIfc pai = (ProtocolAdapterIfc) invocationTarget.getThis(); try { o = pai.getRemoteSensorValue(); } catch (CommChecksumFault csf) { throw new SoftenedProtocolException( "protocol error [checksum error]: " + csf.getMessage()); } catch (CommConnectFailure cf) { throw new SoftenedProtocolException( "protocol error [comm failure]: " + cf.getMessage()); } catch (CommPacketSequenceFault psf) { throw new SoftenedProtocolException( "protocol error [message sequence error]" + psf.getMessage()); } return o; }}
通過實現Spring MethodInterceptor接口并將該類插入Spring系統(稍后我們將進行討論),我們將通過MethodInvocation參數獲得實際的目標方法。這樣就能提取我們想要的真正對象。請記住,我們更改了被調用者看作SensorIfc的接口,該接口使目標對象得以實現ProtocolAdapterIfc。我們這樣做是為了簡化調用,更是為了消除所有的已檢查異常。該攔截器只調用用來捕獲可能拋出的任何已檢查異常、并用SoftenedProtocolException將它們重新包裝的目標方法。實際上,我們只重新包裝消息,但要做到心中有數。SoftenedProtocolException擴展了RuntimeException,后者無疑是一個未檢查異常。該操作的最終結果是應用程序開發人員不必處理任何已檢查異常。如果他們真想處理異常,他們只需(非強制地)處理一個:SoftenedProtocolException。不錯吧!
那么,如何才能讓Spring完成這一切呢?答案就是要有正確的配置文件。我們現在就來看一看該配置文件,并了解其工作原理:
<!-- TARGET OBJECT --><bean id="protocolAdapter" class="yourco.project.comm.CarrierPigeon"> <property name="isTired"> <value>true</value> </property≷ <property name="canFlapWings"> <value>true</value> </property≷</bean≷<!-- INTERCEPTOR --><bean id="sensorInterceptor" class="yourco.project.springsupport. SensorInvocationInterceptor"/><!--WIRE EVERYTHING UP, HAND BACK TO THE USER--><bean id="temperatureSensorOne" class="org.springframework.aop.framework. ProxyFactoryBean"> <property name="proxyInterfaces"> <value>yourco.project.interfaces.SensorIfc </value> </property> <property name="target"> <ref local="protocolAdapter"/> </property> <property name="interceptorNames"> <list> <value>sensorInterceptor</value> </list> </property></bean>
我們逐節看這些代碼時,可以看到它比我們前面看到的Spring配置文件要稍微復雜一些?!癟ARGET OBJECT”注釋下面的第一節指定了要作為調用的目標對象的類。還記得攔截器曾將一個參數傳入其中來表示目標對象嗎?這就是其工作原理?;谠搮?,Spring現在知道要將哪個對象傳遞進來。“INTERCEPTOR”下面的代碼節是在目標方法之前調用的類。此時,如果目標類拋出已檢查異常,要開始處理異常,并進行軟化。Spring將它與目標類相關聯。最后一節是將各種元素聯系起來。用戶要請求的bean位于bean鍵temperatureSensorOne下。ProxyFactoryBean將生成并返回一個代理類,它用來實現yourco.project.interfaces.SensorIfc接口。目標對象當然就是protocolAdapter bean,它由yourco.project.comm.CarrierPigeon的實例支持。只要用戶調用代理的方法就插入并調用的攔截器位于bean鍵sensorInterceptor下,并由yourco.project.springsupport.SensorInvocationInterceptor支持。請注意,可使用多個攔截器,因為該屬性是一個列表,因此可以將許多攔截器對象關聯到方法調用中,這是一個非常有用的理念。
運行該應用程序時,根據插入的CarrierPigeon值,我們可以看到一些有趣的行為。如果我們的CarrierPigeon沒有使用過度并能執行,我們將看到這樣的輸出:
The sensor says the temp is:42
顯然“信鴿”沒問題而且狀況很好,并計算出溫度為42。如果由于改變CarrierPigeon Spring節中的值,而造成“信鴿”使用過度或不能執行,我們將得到如下所示的結果:
yourco.project.exceptions.comm.SoftenedProtocolException: protocol error [comm failure]: I'm Tired! at yourco.project.springsupport.SensorInvocationInterceptor.invoke (SensorInvocationInterceptor.java:57) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed (ReflectiveMethodInvocation.java:144) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke (JdkDynamicAopProxy.java:174) at .getSensorValue(Unknown Source) at yourco.project.main.Main.main(Main.java:32)
在這種情況下,“信鴿”或者使用過度,或者不能執行,因此得到的結果是SoFTProtocolException,并附帶一條消息說明發生的情況:“I'm tired!”夠酷吧!
我希望人們能夠開始了解Spring的強大及其多種功能。Spring框架悄然興起,并成為開發人員的編程工具箱中的真正的“瑞士軍刀”。Spring在實現讓開發人員能夠集中于應用程序的基本組成部分這一傳說般的承諾方面做得不錯,這也正是它得以大行其道的業務邏輯。Spring將您從J2EE中的一些錯綜復雜的方面中解放出來。Spring的強大在于它能夠使大多數東西看起來就如同非常普通的Java對象一樣,而不管它們的性質或來源如何。既然Spring自身肩負起創建、關聯和配置的重擔,那么開發人員要做的只是掌握使用對象而不是構造對象的方法。Spring就如同在雜貨店購買的預煮的飯菜。您要做的只是決定想吃什么、把它帶回家、加熱,然后吃!
綜合說明
Spring是一個非常健壯的輕量級框架,它極好地彌補了J2EE/EJB環境的不足。Spring真正偉大的一點在于它不走極端。您可以以一種非常簡單的方式開始使用Spring(正如我所做的那樣),只是建立常見的關聯,作為定位器模式的一種實現。稍后,您將發現其驚人的功能,并且很快對它的要求會越來越多。
我所發現的一個驚人之處是Spring所提供的測試靈活性。現在人們對通過Junit進行單元測試日益重視,這增加了測試,而不是減少測試。J2EE容器的使用使測試極端復雜,以至于難以進行。這種困難局面源于業務邏輯和容器框架服務之間產生的耦合。借助于Spring的配置機制,使實現可以動態切換,這樣就有助于將業務對象從容器中釋放出來。我們已經看到,如果只想測試Web組件,將活動的EJB換成其代理或一個已清除的業務服務并不會造成太大影響。借助于JDBC或Hibernate數據訪問對象,我們可以使用常見的簡單JDBC、非XA的數據源連接來測試這些組件,并將它們無縫地換出,代之以健壯的基于JTA、JNDI的對應連接。結論是:如果代碼易于測試,并因此測試得更多,那么質量必然會提高。
結束語
本文粗略地概括介紹了IoC,并詳細介紹了Spring。Spring框架具有許多功能,其中許多功能在本文中只是點到為止。從基本的依賴注入到復雜的AOP操作,這些組成了Spring的強大功能,這是它的主要優點之一。能夠根據問題需要使用或多或少的IoC功能是一個極具吸引力的理念,我認為,這在通常錯綜復雜的J2EE編程領域也是頗受歡迎的?,F在看完了這篇文章,我衷心地希望它能對您有所幫助,并可以應用到您的工作中。歡迎隨時提供反饋!
參考資料
(出處:http://www.companysz.com)
新聞熱點
疑難解答